@zonease/aiworker-cli 0.12.2 → 0.13.0

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 (117) hide show
  1. package/README.md +158 -350
  2. package/aiworker-bun.js +614 -694
  3. package/drizzle/worker/0000_polite_stellaris.sql +219 -0
  4. package/drizzle/worker/0001_red_lady_mastermind.sql +29 -0
  5. package/drizzle/worker/0002_concerned_slyde.sql +45 -0
  6. package/drizzle/worker/meta/0000_snapshot.json +1055 -194
  7. package/drizzle/worker/meta/0001_snapshot.json +1244 -222
  8. package/drizzle/worker/meta/0002_snapshot.json +1557 -273
  9. package/drizzle/worker/meta/_journal.json +6 -48
  10. package/official-apps/aiworker-hr/README.md +16 -0
  11. package/official-apps/aiworker-hr/capabilities/candidate-screen/prompt.md +3 -0
  12. package/official-apps/aiworker-hr/capabilities/candidate-screen/review.md +5 -0
  13. package/official-apps/aiworker-hr/capabilities/person-profile/prompt.md +3 -0
  14. package/official-apps/aiworker-hr/capabilities/person-profile/review.md +5 -0
  15. package/official-apps/aiworker-hr/dist/host-mounted.js +15677 -0
  16. package/official-apps/aiworker-hr/dist/index.js +15411 -0
  17. package/official-apps/aiworker-hr/dist/standalone.js +15451 -0
  18. package/official-apps/aiworker-hr/migrations/0001_hr.sql +2 -0
  19. package/official-apps/aiworker-hr/package.json +31 -0
  20. package/official-apps/aiworker-hr/packs/hr-recruiting/SOUL.md +7 -0
  21. package/official-apps/aiworker-hr/review/candidate-screen.md +5 -0
  22. package/official-apps/aiworker-hr/review/person-profile.md +5 -0
  23. package/official-apps/aiworker-hr/schemas/candidate-screen.schema.json +50 -0
  24. package/official-apps/aiworker-hr/schemas/person-profile.schema.json +50 -0
  25. package/official-apps/aiworker-hr/soul-app.manifest.json +374 -0
  26. package/official-apps/aiworker-hr/src/api.ts +1 -0
  27. package/official-apps/aiworker-hr/src/host-mounted.ts +308 -0
  28. package/official-apps/aiworker-hr/src/index.ts +152 -0
  29. package/official-apps/aiworker-hr/src/protocol/artifact.ts +2 -0
  30. package/official-apps/aiworker-hr/src/protocol/connectors.ts +2 -0
  31. package/official-apps/aiworker-hr/src/protocol/lifecycle.ts +2 -0
  32. package/official-apps/aiworker-hr/src/protocol/review.ts +2 -0
  33. package/official-apps/aiworker-hr/src/protocol/runtime.ts +2 -0
  34. package/official-apps/aiworker-hr/src/protocol/ui.ts +2 -0
  35. package/official-apps/aiworker-hr/src/standalone.ts +43 -0
  36. package/official-apps/aiworker-hr/src/ui/candidate-screen-preview.tsx +2 -0
  37. package/official-apps/aiworker-hr/src/ui/hr-route.tsx +1 -0
  38. package/official-apps/aiworker-hr/src/ui/people-widget.tsx +1 -0
  39. package/official-apps/aiworker-hr/src/ui/person-profile-preview.tsx +2 -0
  40. package/official-apps/aiworker-hr/src/ui/profile-panel.tsx +1 -0
  41. package/official-apps/aiworker-hr/src/ui/review-panel.tsx +1 -0
  42. package/official-apps/aiworker-hr/tsconfig.json +20 -0
  43. package/official-apps/aiworker-qa/README.md +14 -0
  44. package/official-apps/aiworker-qa/capabilities/regression-matrix/prompt.md +3 -0
  45. package/official-apps/aiworker-qa/capabilities/regression-matrix/review.md +5 -0
  46. package/official-apps/aiworker-qa/capabilities/release-gate/prompt.md +3 -0
  47. package/official-apps/aiworker-qa/capabilities/release-gate/review.md +5 -0
  48. package/official-apps/aiworker-qa/dist/host-mounted.js +15655 -0
  49. package/official-apps/aiworker-qa/dist/index.js +15395 -0
  50. package/official-apps/aiworker-qa/dist/standalone.js +15435 -0
  51. package/official-apps/aiworker-qa/migrations/0001_qa.sql +2 -0
  52. package/official-apps/aiworker-qa/package.json +31 -0
  53. package/official-apps/aiworker-qa/packs/qa-reviewer/SOUL.md +7 -0
  54. package/official-apps/aiworker-qa/review/regression-matrix.md +5 -0
  55. package/official-apps/aiworker-qa/review/release-gate.md +5 -0
  56. package/official-apps/aiworker-qa/schemas/regression-matrix.schema.json +50 -0
  57. package/official-apps/aiworker-qa/schemas/release-gate.schema.json +50 -0
  58. package/official-apps/aiworker-qa/soul-app.manifest.json +356 -0
  59. package/official-apps/aiworker-qa/src/api.ts +1 -0
  60. package/official-apps/aiworker-qa/src/host-mounted.ts +302 -0
  61. package/official-apps/aiworker-qa/src/index.ts +152 -0
  62. package/official-apps/aiworker-qa/src/protocol/artifact.ts +2 -0
  63. package/official-apps/aiworker-qa/src/protocol/connectors.ts +2 -0
  64. package/official-apps/aiworker-qa/src/protocol/lifecycle.ts +2 -0
  65. package/official-apps/aiworker-qa/src/protocol/review.ts +2 -0
  66. package/official-apps/aiworker-qa/src/protocol/runtime.ts +2 -0
  67. package/official-apps/aiworker-qa/src/protocol/ui.ts +2 -0
  68. package/official-apps/aiworker-qa/src/standalone.ts +43 -0
  69. package/official-apps/aiworker-qa/src/ui/qa-route.tsx +1 -0
  70. package/official-apps/aiworker-qa/src/ui/regression-matrix-preview.tsx +2 -0
  71. package/official-apps/aiworker-qa/src/ui/release-gate-preview.tsx +2 -0
  72. package/official-apps/aiworker-qa/src/ui/release-panel.tsx +1 -0
  73. package/official-apps/aiworker-qa/src/ui/release-review-panel.tsx +1 -0
  74. package/official-apps/aiworker-qa/src/ui/release-widget.tsx +1 -0
  75. package/official-apps/aiworker-qa/src/ui/review-panel.tsx +1 -0
  76. package/official-apps/aiworker-qa/tsconfig.json +20 -0
  77. package/package.json +5 -4
  78. package/web/worker/assets/index-ByOwFiyz.js +18 -0
  79. package/web/worker/assets/index-K-y56wrL.css +2 -0
  80. package/web/worker/assets/markdown-preview-DFe-rfff.js +29 -0
  81. package/web/worker/assets/people-workbench-V1Ajqfzv.js +1 -0
  82. package/web/worker/engine-icons/claude.svg +1 -0
  83. package/web/worker/engine-icons/cursor.svg +1 -0
  84. package/web/worker/engine-icons/gemini.svg +1 -0
  85. package/web/worker/engine-icons/hermesagent.svg +1 -0
  86. package/web/worker/engine-icons/openai.svg +1 -0
  87. package/web/worker/engine-icons/opencode.svg +1 -0
  88. package/web/worker/engine-icons/qwen.svg +1 -0
  89. package/web/worker/fonts/inter-latin-wght-normal.woff2 +0 -0
  90. package/web/worker/fonts/jetbrains-mono-latin-wght-normal.woff2 +0 -0
  91. package/web/worker/fonts/nunito-latin-wght-normal.woff2 +0 -0
  92. package/web/worker/index.html +8 -4
  93. package/web/worker/logo.svg +8 -0
  94. package/drizzle/fleet/0000_fine_havok.sql +0 -23
  95. package/drizzle/fleet/meta/0000_snapshot.json +0 -165
  96. package/drizzle/fleet/meta/_journal.json +0 -13
  97. package/drizzle/worker/0000_spooky_kat_farrell.sql +0 -112
  98. package/drizzle/worker/0001_secret_dagger.sql +0 -1
  99. package/drizzle/worker/0002_jazzy_moondragon.sql +0 -13
  100. package/drizzle/worker/0003_rare_cloak.sql +0 -7
  101. package/drizzle/worker/0004_daffy_thing.sql +0 -26
  102. package/drizzle/worker/0005_worthless_whiplash.sql +0 -20
  103. package/drizzle/worker/0006_fair_jetstream.sql +0 -34
  104. package/drizzle/worker/0007_solid_bromley.sql +0 -11
  105. package/drizzle/worker/0008_peaceful_titanium_man.sql +0 -14
  106. package/drizzle/worker/meta/0003_snapshot.json +0 -873
  107. package/drizzle/worker/meta/0004_snapshot.json +0 -1058
  108. package/drizzle/worker/meta/0005_snapshot.json +0 -1192
  109. package/drizzle/worker/meta/0006_snapshot.json +0 -1420
  110. package/drizzle/worker/meta/0007_snapshot.json +0 -1489
  111. package/drizzle/worker/meta/0008_snapshot.json +0 -1593
  112. package/web/fleet/assets/index-BTknRPEg.js +0 -1372
  113. package/web/fleet/assets/index-lu-9OhC0.css +0 -2
  114. package/web/fleet/favicon.svg +0 -4
  115. package/web/fleet/index.html +0 -14
  116. package/web/worker/assets/index-DuxsPbd7.js +0 -1382
  117. package/web/worker/assets/index-lu-9OhC0.css +0 -2
@@ -0,0 +1,302 @@
1
+ import { Buffer } from 'node:buffer'
2
+ import process from 'node:process'
3
+
4
+ import { createSoulAppClient } from '@zonease/aiworker-soul-app-sdk'
5
+
6
+ import { qaReferenceSoulApp, qaSoulAppManifest } from './index'
7
+
8
+ interface MountContext {
9
+ brokerUrl?: string
10
+ hostUrl?: string
11
+ operatorId?: string | null
12
+ sessionId?: string | null
13
+ surface?: {
14
+ id?: string
15
+ scope?: string
16
+ }
17
+ workerId?: string | null
18
+ workspaceId?: string | null
19
+ }
20
+
21
+ interface BrokerSearchResult {
22
+ items?: Array<Record<string, unknown>>
23
+ }
24
+
25
+ export function serveHostMounted(port = Number(Bun.env.PORT ?? 0)) {
26
+ return Bun.serve({
27
+ async fetch(request) {
28
+ const url = new URL(request.url)
29
+ if (url.pathname === '/health') {
30
+ return Response.json({
31
+ appId: qaSoulAppManifest.id,
32
+ mode: 'host-mounted',
33
+ status: 'ok',
34
+ })
35
+ }
36
+ const tokenError = verifyMountToken(request)
37
+ if (tokenError)
38
+ return tokenError
39
+ if (url.pathname === '/domain') {
40
+ return Response.json({
41
+ appId: qaSoulAppManifest.id,
42
+ capabilities: qaSoulAppManifest.capabilities.map(capability => capability.id),
43
+ mounted: true,
44
+ soul: qaSoulAppManifest.soul.id,
45
+ workspaceTypes: qaSoulAppManifest.workspaceTypes.map(type => type.id),
46
+ })
47
+ }
48
+ if (url.pathname === '/surfaces/routes/qa-home' || url.pathname === '/surfaces/panels/qa-release-panel') {
49
+ return Response.json(qaDescriptorSurface(request, url.pathname))
50
+ }
51
+ if (url.pathname === '/frames/widgets/qa-release-widget') {
52
+ return new Response(qaWidgetFrameHtml(request), {
53
+ headers: { 'content-type': 'text/html; charset=utf-8' },
54
+ })
55
+ }
56
+ if (url.pathname === '/protocol/actions' && request.method === 'POST') {
57
+ const body = await request.json().catch(() => ({})) as Record<string, unknown>
58
+ return Response.json(await qaProtocolAction(request, String(body.protocolAction ?? '')))
59
+ }
60
+ if (url.pathname === '/protocol/search' && request.method === 'GET') {
61
+ return Response.json(await qaProtocolSearch(request, url))
62
+ }
63
+ if (url.pathname === '/broker/permissions') {
64
+ const hostUrl = request.headers.get('x-aiworker-host-url') ?? Bun.env.AIWORKER_HOST_URL
65
+ if (!hostUrl)
66
+ return Response.json({ appId: qaSoulAppManifest.id, broker: 'not-configured', permissions: [] })
67
+ const client = createSoulAppClient({ appId: qaSoulAppManifest.id, baseUrl: hostUrl })
68
+ return Response.json(await client.broker.permissions.list())
69
+ }
70
+ if (url.pathname === '/protocol/capabilities') {
71
+ return Response.json({
72
+ capabilities: await qaReferenceSoulApp.ui?.capabilities({
73
+ appId: qaSoulAppManifest.id,
74
+ permissions: qaSoulAppManifest.permissions,
75
+ }),
76
+ })
77
+ }
78
+ return Response.json({ error: { code: 'NOT_FOUND', message: `Unknown QA app route: ${url.pathname}` } }, { status: 404 })
79
+ },
80
+ hostname: Bun.env.HOST ?? '127.0.0.1',
81
+ port,
82
+ })
83
+ }
84
+
85
+ if (import.meta.main) {
86
+ const server = serveHostMounted()
87
+ process.stdout.write(`${JSON.stringify({ appId: qaSoulAppManifest.id, mode: 'host-mounted', url: `http://${server.hostname}:${server.port}` })}\n`)
88
+ }
89
+
90
+ function verifyMountToken(request: Request): Response | null {
91
+ const expected = Bun.env.AIWORKER_MOUNT_TOKEN
92
+ if (!expected)
93
+ return null
94
+ const actual = request.headers.get('x-aiworker-mount-token')
95
+ return actual === expected
96
+ ? null
97
+ : Response.json({ error: { code: 'INVALID_MOUNT_TOKEN', message: 'Host mount token is required.' } }, { status: 401 })
98
+ }
99
+
100
+ function qaDescriptorSurface(request: Request, pathname: string) {
101
+ const context = readMountContext(request)
102
+ return {
103
+ actions: [
104
+ {
105
+ id: 'create-release-review',
106
+ label: 'Create review',
107
+ method: 'POST',
108
+ target: `${context?.brokerUrl ?? '/broker'}/reviews`,
109
+ },
110
+ ],
111
+ appId: qaSoulAppManifest.id,
112
+ authority: 'soul-app',
113
+ cache: {
114
+ freshness: 'non-authoritative',
115
+ },
116
+ context: {
117
+ signed: Boolean(request.headers.get('x-aiworker-mount-signature')),
118
+ surfaceId: context?.surface?.id ?? null,
119
+ workspaceId: context?.workspaceId ?? null,
120
+ },
121
+ fields: [
122
+ { label: 'Domain', value: qaSoulAppManifest.soul.domain },
123
+ { label: 'Workspace types', value: qaSoulAppManifest.workspaceTypes.map(type => type.name).join(', ') },
124
+ { label: 'Evidence broker', value: qaSoulAppManifest.connectors.required.map(connector => connector.id).join(', ') },
125
+ ],
126
+ path: pathname,
127
+ renderer: 'host-descriptor',
128
+ status: 'ready',
129
+ title: pathname.includes('/routes/') ? 'QA Mounted Workbench' : 'Release Gate Panel',
130
+ type: 'aiworker.surface.descriptor.v1',
131
+ }
132
+ }
133
+
134
+ function qaWidgetFrameHtml(request: Request): string {
135
+ const context = readMountContext(request)
136
+ return [
137
+ '<!doctype html>',
138
+ '<html lang="en">',
139
+ '<head><meta charset="utf-8"><title>QA Release Widget</title></head>',
140
+ '<body>',
141
+ '<main>',
142
+ '<h1>Release Widget</h1>',
143
+ `<p data-soul-app-id="${qaSoulAppManifest.id}">Mounted QA frame surface</p>`,
144
+ `<p data-surface-id="${context?.surface?.id ?? 'unknown'}">Scope ${context?.surface?.scope ?? 'workspace'}</p>`,
145
+ '</main>',
146
+ '</body>',
147
+ '</html>',
148
+ ].join('')
149
+ }
150
+
151
+ async function qaProtocolAction(request: Request, protocolAction: string) {
152
+ if (protocolAction === 'releaseGates.create') {
153
+ const persisted = await persistReleaseGateDraft(request)
154
+ if (!persisted.ok)
155
+ return persisted
156
+ return {
157
+ message: 'Release gate draft opened by QA app.',
158
+ ok: true,
159
+ redirectTo: '/qa/release',
160
+ refresh: true,
161
+ }
162
+ }
163
+ if (protocolAction === 'release.refresh') {
164
+ return {
165
+ message: 'Release data refreshed by QA app.',
166
+ ok: true,
167
+ refresh: true,
168
+ }
169
+ }
170
+ if (protocolAction === 'settings.open') {
171
+ return {
172
+ message: 'QA settings are owned by the QA app.',
173
+ ok: true,
174
+ }
175
+ }
176
+ return {
177
+ message: `Unknown QA protocol action: ${protocolAction}`,
178
+ ok: false,
179
+ }
180
+ }
181
+
182
+ async function persistReleaseGateDraft(request: Request): Promise<{ message: string, ok: false } | { ok: true }> {
183
+ const context = readMountContext(request)
184
+ if (!context?.hostUrl)
185
+ return { ok: true }
186
+
187
+ const draftKey = `drafts/release-gate/${context.workspaceId ?? 'app'}`
188
+ const workspaceRef = context.workspaceId ?? 'app'
189
+ const client = createSoulAppClient({ appId: qaSoulAppManifest.id, baseUrl: context.hostUrl })
190
+ try {
191
+ await client.broker.storage.put(draftKey, {
192
+ appId: qaSoulAppManifest.id,
193
+ kind: 'release-gate',
194
+ source: 'qa-mounted-action',
195
+ status: 'draft',
196
+ workspaceId: context.workspaceId ?? null,
197
+ }, brokerScope(context))
198
+ await client.broker.search.upsert(draftKey, {
199
+ kind: 'release-gate',
200
+ reference: {
201
+ id: workspaceRef,
202
+ type: context.workspaceId ? 'workspace' : 'app',
203
+ },
204
+ sessionId: context.sessionId ?? null,
205
+ summary: `QA app-owned release gate draft for workspace ${workspaceRef}.`,
206
+ title: 'Release gate draft',
207
+ workspaceId: context.workspaceId ?? null,
208
+ }, brokerScope(context))
209
+ return { ok: true }
210
+ }
211
+ catch (error) {
212
+ return {
213
+ message: error instanceof Error ? error.message : String(error),
214
+ ok: false,
215
+ }
216
+ }
217
+ }
218
+
219
+ async function qaProtocolSearch(request: Request, url: URL) {
220
+ const query = url.searchParams.get('query') ?? ''
221
+ const brokerItems = await queryBrokerReleaseSearch(request, query)
222
+ if (brokerItems?.length) {
223
+ return {
224
+ items: brokerItems,
225
+ providerId: 'releases.search',
226
+ }
227
+ }
228
+
229
+ return {
230
+ items: [{
231
+ appId: qaSoulAppManifest.id,
232
+ authority: 'soul-app' as const,
233
+ id: 'release-gate-draft',
234
+ kind: 'release-gate',
235
+ openAction: {
236
+ id: 'create-release-gate',
237
+ input: { query },
238
+ },
239
+ status: 'draft',
240
+ summary: query ? `QA app-owned release match for ${query}` : 'Open QA release gate workspace',
241
+ title: query ? `Release gate: ${query}` : 'Release gate draft',
242
+ }],
243
+ providerId: 'releases.search',
244
+ }
245
+ }
246
+
247
+ async function queryBrokerReleaseSearch(request: Request, query: string): Promise<Array<Record<string, unknown>> | null> {
248
+ const context = readMountContext(request)
249
+ if (!context?.hostUrl)
250
+ return null
251
+
252
+ const client = createSoulAppClient({ appId: qaSoulAppManifest.id, baseUrl: context.hostUrl })
253
+ try {
254
+ const result = await client.broker.search.query(query, brokerScope(context)) as BrokerSearchResult
255
+ return Array.isArray(result.items) ? result.items : null
256
+ }
257
+ catch {
258
+ return null
259
+ }
260
+ }
261
+
262
+ function readMountContext(request: Request): MountContext | null {
263
+ const payload = request.headers.get('x-aiworker-mount-context')
264
+ if (!payload)
265
+ return null
266
+ try {
267
+ const parsed = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8')) as unknown
268
+ if (!isRecord(parsed))
269
+ return null
270
+ const surface = isRecord(parsed.surface) ? parsed.surface : null
271
+ return {
272
+ brokerUrl: typeof parsed.brokerUrl === 'string' ? parsed.brokerUrl : undefined,
273
+ hostUrl: request.headers.get('x-aiworker-host-url') ?? undefined,
274
+ operatorId: typeof parsed.operatorId === 'string' ? parsed.operatorId : null,
275
+ sessionId: typeof parsed.sessionId === 'string' ? parsed.sessionId : null,
276
+ surface: surface
277
+ ? {
278
+ id: typeof surface.id === 'string' ? surface.id : undefined,
279
+ scope: typeof surface.scope === 'string' ? surface.scope : undefined,
280
+ }
281
+ : undefined,
282
+ workerId: typeof parsed.workerId === 'string' ? parsed.workerId : null,
283
+ workspaceId: typeof parsed.workspaceId === 'string' ? parsed.workspaceId : null,
284
+ }
285
+ }
286
+ catch {
287
+ return null
288
+ }
289
+ }
290
+
291
+ function brokerScope(context: MountContext) {
292
+ return {
293
+ operatorId: context.operatorId ?? undefined,
294
+ sessionId: context.sessionId ?? undefined,
295
+ workerId: context.workerId ?? undefined,
296
+ workspaceId: context.workspaceId ?? undefined,
297
+ }
298
+ }
299
+
300
+ function isRecord(value: unknown): value is Record<string, unknown> {
301
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
302
+ }
@@ -0,0 +1,152 @@
1
+ import type {
2
+ SoulAppArtifactValidationResult,
3
+ SoulAppCapability,
4
+ SoulAppDefinition,
5
+ SoulAppProtocolResult,
6
+ SoulAppScopedContext,
7
+ SoulAppSessionContext,
8
+ } from '@zonease/aiworker-soul-app-sdk'
9
+
10
+ import { createSoulAppManifest, defineSoulApp, parseNamespacedSoulAppCapabilityId } from '@zonease/aiworker-soul-app-sdk'
11
+
12
+ import manifestJson from '../soul-app.manifest.json' with { type: 'json' }
13
+
14
+ export const qaSoulAppManifest = createSoulAppManifest(manifestJson)
15
+
16
+ export const QA_REFERENCE_APP_BOUNDARY = {
17
+ hostMountedEntry: './src/host-mounted.ts',
18
+ packageName: '@zonease/aiworker-qa',
19
+ primaryWorkbench: 'Release Gate Workbench',
20
+ standaloneEntry: './src/standalone.ts',
21
+ } as const
22
+
23
+ export const qaReferenceSoulApp: SoulAppDefinition = defineSoulApp({
24
+ artifact: {
25
+ async artifactSchemas() {
26
+ return qaSoulAppManifest.artifactTypes
27
+ },
28
+ async extractMetadata(_context, artifact) {
29
+ return {
30
+ appId: qaSoulAppManifest.id,
31
+ contentRef: artifact.contentRef,
32
+ kind: artifact.type,
33
+ releaseRisk: artifact.type === 'release-gate' ? 'gate-review' : 'coverage-review',
34
+ }
35
+ },
36
+ async validateArtifact(_context, artifact) {
37
+ return validateArtifactType(artifact.type)
38
+ },
39
+ },
40
+ connector: {
41
+ async declareConnectorNeeds() {
42
+ return [
43
+ ...qaSoulAppManifest.connectors.required,
44
+ ...qaSoulAppManifest.connectors.optional,
45
+ ]
46
+ },
47
+ },
48
+ lifecycle: lifecycleHandlers('QA reference app ready.'),
49
+ manifest: qaSoulAppManifest,
50
+ review: {
51
+ async createReviewRubric(_context, artifactType) {
52
+ return {
53
+ checks: [
54
+ `Artifact type ${artifactType} maps test evidence to release risk.`,
55
+ 'Known defects, missing evidence, and residual risk are separated.',
56
+ 'Go/no-go recommendation is explicit and reviewable.',
57
+ ],
58
+ policyRef: qaSoulAppManifest.artifactTypes.find(type => type.id === artifactType)?.reviewPolicyRef,
59
+ }
60
+ },
61
+ async proposeMemoryCandidate(context, review) {
62
+ return {
63
+ evidence: [{
64
+ appId: context.appId,
65
+ artifactId: review.artifactId,
66
+ reviewId: review.reviewId,
67
+ source: 'qa-reference-app',
68
+ }],
69
+ statement: 'Promote reviewed QA release gate guidance into the QA Soul namespace.',
70
+ }
71
+ },
72
+ },
73
+ runtime: {
74
+ async prepareSessionContext(context, input) {
75
+ const capability = resolveQaCapability(input.capabilityId)
76
+ return sessionContext(context, capability, input.workspaceType)
77
+ },
78
+ async resolveCapability(_context, input) {
79
+ return resolveQaCapability(input.capabilityId ?? input.intent)
80
+ },
81
+ },
82
+ ui: {
83
+ async artifactTypes() {
84
+ return qaSoulAppManifest.artifactTypes
85
+ },
86
+ async capabilities() {
87
+ return qaSoulAppManifest.capabilities
88
+ },
89
+ async ui() {
90
+ return qaSoulAppManifest.ui
91
+ },
92
+ async workspaceTypes() {
93
+ return qaSoulAppManifest.workspaceTypes
94
+ },
95
+ },
96
+ })
97
+
98
+ function resolveQaCapability(input?: string): SoulAppCapability {
99
+ const id = normalizeCapabilityId(input) ?? qaSoulAppManifest.capabilities[0]!.id
100
+ const capability = qaSoulAppManifest.capabilities.find(item => item.id === id)
101
+ if (!capability)
102
+ throw new Error(`QA capability not found: ${input}`)
103
+ return capability
104
+ }
105
+
106
+ function normalizeCapabilityId(input?: string): string | null {
107
+ if (!input)
108
+ return null
109
+ return parseNamespacedSoulAppCapabilityId(input)?.capabilityId ?? input
110
+ }
111
+
112
+ function sessionContext(context: SoulAppScopedContext, capability: SoulAppCapability, workspaceType: string): SoulAppSessionContext {
113
+ return {
114
+ artifactTypes: capability.artifactTypes,
115
+ capabilityId: capability.id,
116
+ contextMarkdown: [
117
+ '# QA Soul App Context',
118
+ `App: ${context.appId}`,
119
+ `Workspace type: ${workspaceType}`,
120
+ 'Use release, test suite, defect evidence, regression matrix, and release gate language.',
121
+ ].join('\n'),
122
+ promptFragments: [
123
+ `Use QA capability ${capability.name}.`,
124
+ 'Map test evidence and known defects to user-facing release risk.',
125
+ 'Return a reviewable QA business artifact, not a generic chat answer.',
126
+ ],
127
+ reviewRubric: [
128
+ 'Coverage maps to changed scope and user risk.',
129
+ 'Known blockers and residual risk are separate.',
130
+ 'Recommendation is explicit and actionable.',
131
+ ],
132
+ }
133
+ }
134
+
135
+ function validateArtifactType(type: string): SoulAppArtifactValidationResult {
136
+ const known = qaSoulAppManifest.artifactTypes.some(item => item.id === type)
137
+ return {
138
+ issues: known ? [] : [{ message: `Unknown QA artifact type: ${type}`, severity: 'error' }],
139
+ ok: known,
140
+ }
141
+ }
142
+
143
+ function lifecycleHandlers(message: string) {
144
+ const ok = async (): Promise<SoulAppProtocolResult> => ({ message, ok: true })
145
+ return {
146
+ disable: ok,
147
+ enable: ok,
148
+ healthcheck: ok,
149
+ install: ok,
150
+ upgrade: ok,
151
+ }
152
+ }
@@ -0,0 +1,2 @@
1
+ export const protocolSurface = 'artifact'
2
+ export { qaReferenceSoulApp as soulApp } from '../index'
@@ -0,0 +1,2 @@
1
+ export const protocolSurface = 'connectors'
2
+ export { qaReferenceSoulApp as soulApp } from '../index'
@@ -0,0 +1,2 @@
1
+ export const protocolSurface = 'lifecycle'
2
+ export { qaReferenceSoulApp as soulApp } from '../index'
@@ -0,0 +1,2 @@
1
+ export const protocolSurface = 'review'
2
+ export { qaReferenceSoulApp as soulApp } from '../index'
@@ -0,0 +1,2 @@
1
+ export const protocolSurface = 'runtime'
2
+ export { qaReferenceSoulApp as soulApp } from '../index'
@@ -0,0 +1,2 @@
1
+ export const protocolSurface = 'ui'
2
+ export { qaReferenceSoulApp as soulApp } from '../index'
@@ -0,0 +1,43 @@
1
+ import process from 'node:process'
2
+
3
+ import { qaSoulAppManifest } from './index'
4
+
5
+ export function renderStandaloneHtml(): string {
6
+ return [
7
+ '<!doctype html>',
8
+ '<html lang="en">',
9
+ '<head><meta charset="utf-8"><title>AIWorker QA</title></head>',
10
+ `<body data-soul-app-id="${qaSoulAppManifest.id}">`,
11
+ '<main>',
12
+ `<h1>${qaSoulAppManifest.name}</h1>`,
13
+ `<p>${qaSoulAppManifest.description}</p>`,
14
+ '</main>',
15
+ '</body>',
16
+ '</html>',
17
+ ].join('')
18
+ }
19
+
20
+ export function serveStandalone(port = Number(Bun.env.PORT ?? 0)) {
21
+ return Bun.serve({
22
+ fetch(request) {
23
+ const url = new URL(request.url)
24
+ if (url.pathname === '/health') {
25
+ return Response.json({
26
+ appId: qaSoulAppManifest.id,
27
+ mode: 'standalone',
28
+ status: 'ok',
29
+ })
30
+ }
31
+ return new Response(renderStandaloneHtml(), {
32
+ headers: { 'content-type': 'text/html; charset=utf-8' },
33
+ })
34
+ },
35
+ hostname: Bun.env.HOST ?? '127.0.0.1',
36
+ port,
37
+ })
38
+ }
39
+
40
+ if (import.meta.main) {
41
+ const server = serveStandalone()
42
+ process.stdout.write(`${JSON.stringify({ appId: qaSoulAppManifest.id, mode: 'standalone', url: `http://${server.hostname}:${server.port}` })}\n`)
43
+ }
@@ -0,0 +1 @@
1
+ export const routeId = 'qa-home'
@@ -0,0 +1,2 @@
1
+ export const artifactPreviewId = 'regression-matrix'
2
+ export const artifactPreviewLabel = 'Regression Matrix'
@@ -0,0 +1,2 @@
1
+ export const artifactPreviewId = 'release-gate'
2
+ export const artifactPreviewLabel = 'Release Gate'
@@ -0,0 +1 @@
1
+ export const panelId = 'qa-release-panel'
@@ -0,0 +1 @@
1
+ export const panelId = 'qa-review-panel'
@@ -0,0 +1 @@
1
+ export const widgetId = 'qa-release-widget'
@@ -0,0 +1 @@
1
+ export const panelId = 'qa-review-panel'
@@ -0,0 +1,20 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "paths": {
5
+ "@/*": [
6
+ "./src/*"
7
+ ]
8
+ },
9
+ "types": [
10
+ "@types/bun"
11
+ ]
12
+ },
13
+ "include": [
14
+ "src/**/*.ts"
15
+ ],
16
+ "exclude": [
17
+ "node_modules",
18
+ "dist"
19
+ ]
20
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zonease/aiworker-cli",
3
- "version": "0.12.2",
4
- "description": "AIWorker CLI — self-hosted Project Brain and Worker/Fleet aggregation runtime",
3
+ "version": "0.13.0",
4
+ "description": "AIWorker CLI — local Host and vertical Soul workspace runtime",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "repository": {
@@ -13,14 +13,15 @@
13
13
  "access": "public"
14
14
  },
15
15
  "bin": {
16
- "aiworker": "./aiworker.js"
16
+ "aiworker": "aiworker.js"
17
17
  },
18
18
  "files": [
19
19
  "aiworker.js",
20
20
  "aiworker-bun.js",
21
21
  "README.md",
22
22
  "drizzle/",
23
- "web/"
23
+ "web/",
24
+ "official-apps/"
24
25
  ],
25
26
  "engines": {
26
27
  "bun": ">=1.1"