@zonease/aiworker-cli 0.12.2 → 0.13.1
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/README.md +158 -350
- package/aiworker-bun.js +614 -694
- package/drizzle/worker/0000_polite_stellaris.sql +219 -0
- package/drizzle/worker/0001_red_lady_mastermind.sql +29 -0
- package/drizzle/worker/0002_concerned_slyde.sql +45 -0
- package/drizzle/worker/meta/0000_snapshot.json +1055 -194
- package/drizzle/worker/meta/0001_snapshot.json +1244 -222
- package/drizzle/worker/meta/0002_snapshot.json +1557 -273
- package/drizzle/worker/meta/_journal.json +6 -48
- package/official-apps/aiworker-hr/README.md +16 -0
- package/official-apps/aiworker-hr/capabilities/candidate-screen/prompt.md +3 -0
- package/official-apps/aiworker-hr/capabilities/candidate-screen/review.md +5 -0
- package/official-apps/aiworker-hr/capabilities/person-profile/prompt.md +3 -0
- package/official-apps/aiworker-hr/capabilities/person-profile/review.md +5 -0
- package/official-apps/aiworker-hr/dist/host-mounted.js +15677 -0
- package/official-apps/aiworker-hr/dist/index.js +15411 -0
- package/official-apps/aiworker-hr/dist/standalone.js +15451 -0
- package/official-apps/aiworker-hr/migrations/0001_hr.sql +2 -0
- package/official-apps/aiworker-hr/package.json +31 -0
- package/official-apps/aiworker-hr/packs/hr-recruiting/SOUL.md +7 -0
- package/official-apps/aiworker-hr/review/candidate-screen.md +5 -0
- package/official-apps/aiworker-hr/review/person-profile.md +5 -0
- package/official-apps/aiworker-hr/schemas/candidate-screen.schema.json +50 -0
- package/official-apps/aiworker-hr/schemas/person-profile.schema.json +50 -0
- package/official-apps/aiworker-hr/soul-app.manifest.json +374 -0
- package/official-apps/aiworker-hr/src/api.ts +1 -0
- package/official-apps/aiworker-hr/src/host-mounted.ts +308 -0
- package/official-apps/aiworker-hr/src/index.ts +152 -0
- package/official-apps/aiworker-hr/src/protocol/artifact.ts +2 -0
- package/official-apps/aiworker-hr/src/protocol/connectors.ts +2 -0
- package/official-apps/aiworker-hr/src/protocol/lifecycle.ts +2 -0
- package/official-apps/aiworker-hr/src/protocol/review.ts +2 -0
- package/official-apps/aiworker-hr/src/protocol/runtime.ts +2 -0
- package/official-apps/aiworker-hr/src/protocol/ui.ts +2 -0
- package/official-apps/aiworker-hr/src/standalone.ts +43 -0
- package/official-apps/aiworker-hr/src/ui/candidate-screen-preview.tsx +2 -0
- package/official-apps/aiworker-hr/src/ui/hr-route.tsx +1 -0
- package/official-apps/aiworker-hr/src/ui/people-widget.tsx +1 -0
- package/official-apps/aiworker-hr/src/ui/person-profile-preview.tsx +2 -0
- package/official-apps/aiworker-hr/src/ui/profile-panel.tsx +1 -0
- package/official-apps/aiworker-hr/src/ui/review-panel.tsx +1 -0
- package/official-apps/aiworker-hr/tsconfig.json +20 -0
- package/official-apps/aiworker-qa/README.md +14 -0
- package/official-apps/aiworker-qa/capabilities/regression-matrix/prompt.md +3 -0
- package/official-apps/aiworker-qa/capabilities/regression-matrix/review.md +5 -0
- package/official-apps/aiworker-qa/capabilities/release-gate/prompt.md +3 -0
- package/official-apps/aiworker-qa/capabilities/release-gate/review.md +5 -0
- package/official-apps/aiworker-qa/dist/host-mounted.js +15655 -0
- package/official-apps/aiworker-qa/dist/index.js +15395 -0
- package/official-apps/aiworker-qa/dist/standalone.js +15435 -0
- package/official-apps/aiworker-qa/migrations/0001_qa.sql +2 -0
- package/official-apps/aiworker-qa/package.json +31 -0
- package/official-apps/aiworker-qa/packs/qa-reviewer/SOUL.md +7 -0
- package/official-apps/aiworker-qa/review/regression-matrix.md +5 -0
- package/official-apps/aiworker-qa/review/release-gate.md +5 -0
- package/official-apps/aiworker-qa/schemas/regression-matrix.schema.json +50 -0
- package/official-apps/aiworker-qa/schemas/release-gate.schema.json +50 -0
- package/official-apps/aiworker-qa/soul-app.manifest.json +356 -0
- package/official-apps/aiworker-qa/src/api.ts +1 -0
- package/official-apps/aiworker-qa/src/host-mounted.ts +302 -0
- package/official-apps/aiworker-qa/src/index.ts +152 -0
- package/official-apps/aiworker-qa/src/protocol/artifact.ts +2 -0
- package/official-apps/aiworker-qa/src/protocol/connectors.ts +2 -0
- package/official-apps/aiworker-qa/src/protocol/lifecycle.ts +2 -0
- package/official-apps/aiworker-qa/src/protocol/review.ts +2 -0
- package/official-apps/aiworker-qa/src/protocol/runtime.ts +2 -0
- package/official-apps/aiworker-qa/src/protocol/ui.ts +2 -0
- package/official-apps/aiworker-qa/src/standalone.ts +43 -0
- package/official-apps/aiworker-qa/src/ui/qa-route.tsx +1 -0
- package/official-apps/aiworker-qa/src/ui/regression-matrix-preview.tsx +2 -0
- package/official-apps/aiworker-qa/src/ui/release-gate-preview.tsx +2 -0
- package/official-apps/aiworker-qa/src/ui/release-panel.tsx +1 -0
- package/official-apps/aiworker-qa/src/ui/release-review-panel.tsx +1 -0
- package/official-apps/aiworker-qa/src/ui/release-widget.tsx +1 -0
- package/official-apps/aiworker-qa/src/ui/review-panel.tsx +1 -0
- package/official-apps/aiworker-qa/tsconfig.json +20 -0
- package/package.json +5 -4
- package/web/worker/assets/index-K-y56wrL.css +2 -0
- package/web/worker/assets/index-YNnnk64n.js +18 -0
- package/web/worker/assets/markdown-preview-DFe-rfff.js +29 -0
- package/web/worker/assets/people-workbench-BzWwSc2I.js +1 -0
- package/web/worker/engine-icons/claude.svg +1 -0
- package/web/worker/engine-icons/cursor.svg +1 -0
- package/web/worker/engine-icons/gemini.svg +1 -0
- package/web/worker/engine-icons/hermesagent.svg +1 -0
- package/web/worker/engine-icons/openai.svg +1 -0
- package/web/worker/engine-icons/opencode.svg +1 -0
- package/web/worker/engine-icons/qwen.svg +1 -0
- package/web/worker/fonts/inter-latin-wght-normal.woff2 +0 -0
- package/web/worker/fonts/jetbrains-mono-latin-wght-normal.woff2 +0 -0
- package/web/worker/fonts/nunito-latin-wght-normal.woff2 +0 -0
- package/web/worker/index.html +8 -4
- package/web/worker/logo.svg +8 -0
- package/drizzle/fleet/0000_fine_havok.sql +0 -23
- package/drizzle/fleet/meta/0000_snapshot.json +0 -165
- package/drizzle/fleet/meta/_journal.json +0 -13
- package/drizzle/worker/0000_spooky_kat_farrell.sql +0 -112
- package/drizzle/worker/0001_secret_dagger.sql +0 -1
- package/drizzle/worker/0002_jazzy_moondragon.sql +0 -13
- package/drizzle/worker/0003_rare_cloak.sql +0 -7
- package/drizzle/worker/0004_daffy_thing.sql +0 -26
- package/drizzle/worker/0005_worthless_whiplash.sql +0 -20
- package/drizzle/worker/0006_fair_jetstream.sql +0 -34
- package/drizzle/worker/0007_solid_bromley.sql +0 -11
- package/drizzle/worker/0008_peaceful_titanium_man.sql +0 -14
- package/drizzle/worker/meta/0003_snapshot.json +0 -873
- package/drizzle/worker/meta/0004_snapshot.json +0 -1058
- package/drizzle/worker/meta/0005_snapshot.json +0 -1192
- package/drizzle/worker/meta/0006_snapshot.json +0 -1420
- package/drizzle/worker/meta/0007_snapshot.json +0 -1489
- package/drizzle/worker/meta/0008_snapshot.json +0 -1593
- package/web/fleet/assets/index-BTknRPEg.js +0 -1372
- package/web/fleet/assets/index-lu-9OhC0.css +0 -2
- package/web/fleet/favicon.svg +0 -4
- package/web/fleet/index.html +0 -14
- package/web/worker/assets/index-DuxsPbd7.js +0 -1382
- package/web/worker/assets/index-lu-9OhC0.css +0 -2
|
@@ -0,0 +1,308 @@
|
|
|
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 { hrReferenceSoulApp, hrSoulAppManifest } 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: hrSoulAppManifest.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: hrSoulAppManifest.id,
|
|
42
|
+
capabilities: hrSoulAppManifest.capabilities.map(capability => capability.id),
|
|
43
|
+
mounted: true,
|
|
44
|
+
soul: hrSoulAppManifest.soul.id,
|
|
45
|
+
workspaceTypes: hrSoulAppManifest.workspaceTypes.map(type => type.id),
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
if (url.pathname === '/surfaces/routes/hr-home' || url.pathname === '/surfaces/panels/hr-profile-panel') {
|
|
49
|
+
return Response.json(hrDescriptorSurface(request, url.pathname))
|
|
50
|
+
}
|
|
51
|
+
if (url.pathname === '/frames/widgets/hr-people-widget') {
|
|
52
|
+
return new Response(hrWidgetFrameHtml(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 hrProtocolAction(request, String(body.protocolAction ?? '')))
|
|
59
|
+
}
|
|
60
|
+
if (url.pathname === '/protocol/search' && request.method === 'GET') {
|
|
61
|
+
return Response.json(await hrProtocolSearch(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: hrSoulAppManifest.id, broker: 'not-configured', permissions: [] })
|
|
67
|
+
const client = createSoulAppClient({ appId: hrSoulAppManifest.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 hrReferenceSoulApp.ui?.capabilities({
|
|
73
|
+
appId: hrSoulAppManifest.id,
|
|
74
|
+
permissions: hrSoulAppManifest.permissions,
|
|
75
|
+
}),
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
return Response.json({ error: { code: 'NOT_FOUND', message: `Unknown HR 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: hrSoulAppManifest.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 hrDescriptorSurface(request: Request, pathname: string) {
|
|
101
|
+
const context = readMountContext(request)
|
|
102
|
+
return {
|
|
103
|
+
actions: [
|
|
104
|
+
{
|
|
105
|
+
id: 'create-profile-review',
|
|
106
|
+
label: 'Create review',
|
|
107
|
+
method: 'POST',
|
|
108
|
+
target: `${context?.brokerUrl ?? '/broker'}/reviews`,
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
appId: hrSoulAppManifest.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: hrSoulAppManifest.soul.domain },
|
|
123
|
+
{ label: 'Workspace types', value: hrSoulAppManifest.workspaceTypes.map(type => type.name).join(', ') },
|
|
124
|
+
{ label: 'Evidence broker', value: hrSoulAppManifest.connectors.required.map(connector => connector.id).join(', ') },
|
|
125
|
+
],
|
|
126
|
+
path: pathname,
|
|
127
|
+
renderer: 'host-descriptor',
|
|
128
|
+
status: 'ready',
|
|
129
|
+
title: pathname.includes('/routes/') ? 'HR Mounted Workbench' : 'People Profile Panel',
|
|
130
|
+
type: 'aiworker.surface.descriptor.v1',
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function hrWidgetFrameHtml(request: Request): string {
|
|
135
|
+
const context = readMountContext(request)
|
|
136
|
+
return [
|
|
137
|
+
'<!doctype html>',
|
|
138
|
+
'<html lang="en">',
|
|
139
|
+
'<head><meta charset="utf-8"><title>HR People Widget</title></head>',
|
|
140
|
+
'<body>',
|
|
141
|
+
'<main>',
|
|
142
|
+
'<h1>People Widget</h1>',
|
|
143
|
+
`<p data-soul-app-id="${hrSoulAppManifest.id}">Mounted HR 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 hrProtocolAction(request: Request, protocolAction: string) {
|
|
152
|
+
if (protocolAction === 'peopleProfiles.create') {
|
|
153
|
+
const persisted = await persistPeopleProfileDraft(request)
|
|
154
|
+
if (!persisted.ok)
|
|
155
|
+
return persisted
|
|
156
|
+
return {
|
|
157
|
+
message: 'People profile draft opened by HR app.',
|
|
158
|
+
ok: true,
|
|
159
|
+
redirectTo: '/hr/people',
|
|
160
|
+
refresh: true,
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (protocolAction === 'people.refresh') {
|
|
164
|
+
return {
|
|
165
|
+
message: 'People data refreshed by HR app.',
|
|
166
|
+
ok: true,
|
|
167
|
+
refresh: true,
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (protocolAction === 'drawers.evidence.toggle') {
|
|
171
|
+
return {
|
|
172
|
+
message: 'Evidence drawer intent emitted by HR app.',
|
|
173
|
+
ok: true,
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (protocolAction === 'settings.open') {
|
|
177
|
+
return {
|
|
178
|
+
message: 'HR settings are owned by the HR app.',
|
|
179
|
+
ok: true,
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
message: `Unknown HR protocol action: ${protocolAction}`,
|
|
184
|
+
ok: false,
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function persistPeopleProfileDraft(request: Request): Promise<{ message: string, ok: false } | { ok: true }> {
|
|
189
|
+
const context = readMountContext(request)
|
|
190
|
+
if (!context?.hostUrl)
|
|
191
|
+
return { ok: true }
|
|
192
|
+
|
|
193
|
+
const draftKey = `drafts/people-profile/${context.workspaceId ?? 'app'}`
|
|
194
|
+
const workspaceRef = context.workspaceId ?? 'app'
|
|
195
|
+
const client = createSoulAppClient({ appId: hrSoulAppManifest.id, baseUrl: context.hostUrl })
|
|
196
|
+
try {
|
|
197
|
+
await client.broker.storage.put(draftKey, {
|
|
198
|
+
appId: hrSoulAppManifest.id,
|
|
199
|
+
kind: 'people-profile',
|
|
200
|
+
source: 'hr-mounted-action',
|
|
201
|
+
status: 'draft',
|
|
202
|
+
workspaceId: context.workspaceId ?? null,
|
|
203
|
+
}, brokerScope(context))
|
|
204
|
+
await client.broker.search.upsert(draftKey, {
|
|
205
|
+
kind: 'people-profile',
|
|
206
|
+
reference: {
|
|
207
|
+
id: workspaceRef,
|
|
208
|
+
type: context.workspaceId ? 'workspace' : 'app',
|
|
209
|
+
},
|
|
210
|
+
sessionId: context.sessionId ?? null,
|
|
211
|
+
summary: `HR app-owned people profile draft for workspace ${workspaceRef}.`,
|
|
212
|
+
title: 'People profile draft',
|
|
213
|
+
workspaceId: context.workspaceId ?? null,
|
|
214
|
+
}, brokerScope(context))
|
|
215
|
+
return { ok: true }
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
return {
|
|
219
|
+
message: error instanceof Error ? error.message : String(error),
|
|
220
|
+
ok: false,
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function hrProtocolSearch(request: Request, url: URL) {
|
|
226
|
+
const query = url.searchParams.get('query') ?? ''
|
|
227
|
+
const brokerItems = await queryBrokerPeopleProfileSearch(request, query)
|
|
228
|
+
if (brokerItems?.length) {
|
|
229
|
+
return {
|
|
230
|
+
items: brokerItems,
|
|
231
|
+
providerId: 'peopleProfiles.search',
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
items: [{
|
|
237
|
+
appId: hrSoulAppManifest.id,
|
|
238
|
+
authority: 'soul-app' as const,
|
|
239
|
+
id: 'people-profile-draft',
|
|
240
|
+
kind: 'people-profile',
|
|
241
|
+
openAction: {
|
|
242
|
+
id: 'create-people-profile',
|
|
243
|
+
input: { query },
|
|
244
|
+
},
|
|
245
|
+
status: 'draft',
|
|
246
|
+
summary: query ? `HR app-owned profile match for ${query}` : 'Open HR profile workspace',
|
|
247
|
+
title: query ? `People profile: ${query}` : 'People profile draft',
|
|
248
|
+
}],
|
|
249
|
+
providerId: 'peopleProfiles.search',
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function queryBrokerPeopleProfileSearch(request: Request, query: string): Promise<Array<Record<string, unknown>> | null> {
|
|
254
|
+
const context = readMountContext(request)
|
|
255
|
+
if (!context?.hostUrl)
|
|
256
|
+
return null
|
|
257
|
+
|
|
258
|
+
const client = createSoulAppClient({ appId: hrSoulAppManifest.id, baseUrl: context.hostUrl })
|
|
259
|
+
try {
|
|
260
|
+
const result = await client.broker.search.query(query, brokerScope(context)) as BrokerSearchResult
|
|
261
|
+
return Array.isArray(result.items) ? result.items : null
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
return null
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function readMountContext(request: Request): MountContext | null {
|
|
269
|
+
const payload = request.headers.get('x-aiworker-mount-context')
|
|
270
|
+
if (!payload)
|
|
271
|
+
return null
|
|
272
|
+
try {
|
|
273
|
+
const parsed = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8')) as unknown
|
|
274
|
+
if (!isRecord(parsed))
|
|
275
|
+
return null
|
|
276
|
+
const surface = isRecord(parsed.surface) ? parsed.surface : null
|
|
277
|
+
return {
|
|
278
|
+
brokerUrl: typeof parsed.brokerUrl === 'string' ? parsed.brokerUrl : undefined,
|
|
279
|
+
hostUrl: request.headers.get('x-aiworker-host-url') ?? undefined,
|
|
280
|
+
operatorId: typeof parsed.operatorId === 'string' ? parsed.operatorId : null,
|
|
281
|
+
sessionId: typeof parsed.sessionId === 'string' ? parsed.sessionId : null,
|
|
282
|
+
surface: surface
|
|
283
|
+
? {
|
|
284
|
+
id: typeof surface.id === 'string' ? surface.id : undefined,
|
|
285
|
+
scope: typeof surface.scope === 'string' ? surface.scope : undefined,
|
|
286
|
+
}
|
|
287
|
+
: undefined,
|
|
288
|
+
workerId: typeof parsed.workerId === 'string' ? parsed.workerId : null,
|
|
289
|
+
workspaceId: typeof parsed.workspaceId === 'string' ? parsed.workspaceId : null,
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return null
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function brokerScope(context: MountContext) {
|
|
298
|
+
return {
|
|
299
|
+
operatorId: context.operatorId ?? undefined,
|
|
300
|
+
sessionId: context.sessionId ?? undefined,
|
|
301
|
+
workerId: context.workerId ?? undefined,
|
|
302
|
+
workspaceId: context.workspaceId ?? undefined,
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
307
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
308
|
+
}
|
|
@@ -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 hrSoulAppManifest = createSoulAppManifest(manifestJson)
|
|
15
|
+
|
|
16
|
+
export const HR_REFERENCE_APP_BOUNDARY = {
|
|
17
|
+
hostMountedEntry: './src/host-mounted.ts',
|
|
18
|
+
packageName: '@zonease/aiworker-hr',
|
|
19
|
+
primaryWorkbench: 'People/Profile Workbench',
|
|
20
|
+
standaloneEntry: './src/standalone.ts',
|
|
21
|
+
} as const
|
|
22
|
+
|
|
23
|
+
export const hrReferenceSoulApp: SoulAppDefinition = defineSoulApp({
|
|
24
|
+
artifact: {
|
|
25
|
+
async artifactSchemas() {
|
|
26
|
+
return hrSoulAppManifest.artifactTypes
|
|
27
|
+
},
|
|
28
|
+
async extractMetadata(_context, artifact) {
|
|
29
|
+
return {
|
|
30
|
+
appId: hrSoulAppManifest.id,
|
|
31
|
+
contentRef: artifact.contentRef,
|
|
32
|
+
kind: artifact.type,
|
|
33
|
+
lifecycle: artifact.type === 'person-profile' ? 'people-profile' : 'recruiting',
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
async validateArtifact(_context, artifact) {
|
|
37
|
+
return validateArtifactType(artifact.type)
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
connector: {
|
|
41
|
+
async declareConnectorNeeds() {
|
|
42
|
+
return [
|
|
43
|
+
...hrSoulAppManifest.connectors.required,
|
|
44
|
+
...hrSoulAppManifest.connectors.optional,
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
lifecycle: lifecycleHandlers('HR reference app ready.'),
|
|
49
|
+
manifest: hrSoulAppManifest,
|
|
50
|
+
review: {
|
|
51
|
+
async createReviewRubric(_context, artifactType) {
|
|
52
|
+
return {
|
|
53
|
+
checks: [
|
|
54
|
+
`Artifact type ${artifactType} cites source evidence.`,
|
|
55
|
+
'Missing candidate or employee facts are explicit.',
|
|
56
|
+
'Human review notes separate risks, next actions, and memory candidates.',
|
|
57
|
+
],
|
|
58
|
+
policyRef: hrSoulAppManifest.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: 'hr-reference-app',
|
|
68
|
+
}],
|
|
69
|
+
statement: 'Promote reviewed HR evidence handling guidance into the HR Soul namespace.',
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
runtime: {
|
|
74
|
+
async prepareSessionContext(context, input) {
|
|
75
|
+
const capability = resolveHrCapability(input.capabilityId)
|
|
76
|
+
return sessionContext(context, capability, input.workspaceType)
|
|
77
|
+
},
|
|
78
|
+
async resolveCapability(_context, input) {
|
|
79
|
+
return resolveHrCapability(input.capabilityId ?? input.intent)
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
ui: {
|
|
83
|
+
async artifactTypes() {
|
|
84
|
+
return hrSoulAppManifest.artifactTypes
|
|
85
|
+
},
|
|
86
|
+
async capabilities() {
|
|
87
|
+
return hrSoulAppManifest.capabilities
|
|
88
|
+
},
|
|
89
|
+
async ui() {
|
|
90
|
+
return hrSoulAppManifest.ui
|
|
91
|
+
},
|
|
92
|
+
async workspaceTypes() {
|
|
93
|
+
return hrSoulAppManifest.workspaceTypes
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
function resolveHrCapability(input?: string): SoulAppCapability {
|
|
99
|
+
const id = normalizeCapabilityId(input) ?? hrSoulAppManifest.capabilities[0]!.id
|
|
100
|
+
const capability = hrSoulAppManifest.capabilities.find(item => item.id === id)
|
|
101
|
+
if (!capability)
|
|
102
|
+
throw new Error(`HR 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
|
+
'# HR Soul App Context',
|
|
118
|
+
`App: ${context.appId}`,
|
|
119
|
+
`Workspace type: ${workspaceType}`,
|
|
120
|
+
'Use people-profile, candidate-screen, review notes, and source-backed evidence language.',
|
|
121
|
+
].join('\n'),
|
|
122
|
+
promptFragments: [
|
|
123
|
+
`Use HR capability ${capability.name}.`,
|
|
124
|
+
'Preserve candidate or employee evidence provenance and mark missing facts.',
|
|
125
|
+
'Return a reviewable HR business artifact, not a generic chat answer.',
|
|
126
|
+
],
|
|
127
|
+
reviewRubric: [
|
|
128
|
+
'Evidence is source-backed.',
|
|
129
|
+
'Risk and missing evidence are separated.',
|
|
130
|
+
'Next action is concrete for a human HR reviewer.',
|
|
131
|
+
],
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function validateArtifactType(type: string): SoulAppArtifactValidationResult {
|
|
136
|
+
const known = hrSoulAppManifest.artifactTypes.some(item => item.id === type)
|
|
137
|
+
return {
|
|
138
|
+
issues: known ? [] : [{ message: `Unknown HR 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,43 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
|
|
3
|
+
import { hrSoulAppManifest } 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 HR</title></head>',
|
|
10
|
+
`<body data-soul-app-id="${hrSoulAppManifest.id}">`,
|
|
11
|
+
'<main>',
|
|
12
|
+
`<h1>${hrSoulAppManifest.name}</h1>`,
|
|
13
|
+
`<p>${hrSoulAppManifest.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: hrSoulAppManifest.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: hrSoulAppManifest.id, mode: 'standalone', url: `http://${server.hostname}:${server.port}` })}\n`)
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const routeId = 'hr-home'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const widgetId = 'hr-people-widget'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const panelId = 'hr-profile-panel'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const panelId = 'hr-review-panel'
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# AIWorker QA Reference Soul App
|
|
2
|
+
|
|
3
|
+
`@zonease/aiworker-qa` is the reference QA Soul App workspace. It owns the QA
|
|
4
|
+
manifest, protocol handlers, standalone service, Host-mounted service, schemas,
|
|
5
|
+
capabilities, review policy, and release-focused app definition for regression
|
|
6
|
+
matrices, defect evidence, and release gate review.
|
|
7
|
+
|
|
8
|
+
The app depends on the Soul App SDK only. It does not import Host API, CLI,
|
|
9
|
+
Worker Web private modules, raw DB handles, engine adapters, connector tokens,
|
|
10
|
+
vault internals, or sibling Soul Apps.
|
|
11
|
+
|
|
12
|
+
The app is intentionally different from HR: QA workspaces are release/test-suite
|
|
13
|
+
oriented, and its primary artifact is a release gate decision with explicit
|
|
14
|
+
test evidence and residual risk.
|