@bizagent/dsh 0.0.0-stage → 1.0.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 (181) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/LICENSE +21 -0
  3. package/README.md +61 -2
  4. package/cordis.patch.yml +14 -0
  5. package/docs/README.md +23 -0
  6. package/docs/architecture.md +154 -0
  7. package/docs/concepts.md +192 -0
  8. package/docs/configuration.md +154 -0
  9. package/docs/quickstart.md +103 -0
  10. package/docs/quickstart.zh-CN.md +86 -0
  11. package/docs/tools.md +607 -0
  12. package/docs/user-guide.zh-CN.md +132 -0
  13. package/docs/verification.md +69 -0
  14. package/examples/README.md +64 -0
  15. package/examples/demo-runtime.mjs +71 -0
  16. package/examples/learning-lab.mjs +263 -0
  17. package/examples/scenarios.mjs +45 -0
  18. package/lib/agent-preset.d.ts +20 -0
  19. package/lib/agent-preset.js +46 -0
  20. package/lib/agent-session-address.d.ts +14 -0
  21. package/lib/agent-session-address.js +15 -0
  22. package/lib/agent-session-coordinator.d.ts +24 -0
  23. package/lib/agent-session-coordinator.js +101 -0
  24. package/lib/cli.d.ts +3 -0
  25. package/lib/cli.js +142 -0
  26. package/lib/client-types/agent-session-address.d.ts +13 -0
  27. package/lib/client-types/client/agent-mentions.d.ts +3 -0
  28. package/lib/client-types/client/api.d.ts +180 -0
  29. package/lib/client-types/client/components.d.ts +22 -0
  30. package/lib/client-types/client/controller.d.ts +23 -0
  31. package/lib/client-types/client/frame-participants.d.ts +5 -0
  32. package/lib/client-types/client/home-name.d.ts +9 -0
  33. package/lib/client-types/client/human-inbox.d.ts +8 -0
  34. package/lib/client-types/client/index.d.ts +13 -0
  35. package/lib/client-types/client/locales.d.ts +796 -0
  36. package/lib/client-types/client/markdown.d.ts +12 -0
  37. package/lib/client-types/client/project-artifact-link.d.ts +10 -0
  38. package/lib/client-types/client/project-outcome-review-inbox.d.ts +13 -0
  39. package/lib/client-types/client/projects.d.ts +11 -0
  40. package/lib/client-types/client/sample-guide.d.ts +9 -0
  41. package/lib/client-types/client/session-resource.d.ts +34 -0
  42. package/lib/client-types/client/session-surfaces.d.ts +24 -0
  43. package/lib/client-types/client/styles.d.ts +1 -0
  44. package/lib/client-types/config.d.ts +55 -0
  45. package/lib/client-types/cron.d.ts +8 -0
  46. package/lib/client-types/domain.d.ts +818 -0
  47. package/lib/client-types/frame-reference.d.ts +4 -0
  48. package/lib/client-types/home-directory.d.ts +5 -0
  49. package/lib/client-types/home-space.d.ts +49 -0
  50. package/lib/client-types/home-store.d.ts +125 -0
  51. package/lib/client-types/inbox-stalled-topic.d.ts +15 -0
  52. package/lib/client-types/inbox-wake-policy.d.ts +18 -0
  53. package/lib/client-types/known-format.d.ts +2 -0
  54. package/lib/client-types/learning-scheduler.d.ts +36 -0
  55. package/lib/client-types/learning-session-runner.d.ts +49 -0
  56. package/lib/client-types/learning-trigger.d.ts +17 -0
  57. package/lib/client-types/learning-usage.d.ts +17 -0
  58. package/lib/client-types/ledger.d.ts +190 -0
  59. package/lib/client-types/local-owner.d.ts +7 -0
  60. package/lib/client-types/message-source.d.ts +9 -0
  61. package/lib/client-types/organization-location.d.ts +19 -0
  62. package/lib/client-types/project-ledger.d.ts +182 -0
  63. package/lib/client-types/project-seat-context.d.ts +17 -0
  64. package/lib/client-types/safe-file.d.ts +23 -0
  65. package/lib/client-types/sample-organization.d.ts +9 -0
  66. package/lib/client-types/service.d.ts +596 -0
  67. package/lib/client-types/shared-knowledge.d.ts +116 -0
  68. package/lib/client-types/ui-contract.d.ts +276 -0
  69. package/lib/client-types/untrusted-text.d.ts +15 -0
  70. package/lib/client-types/work-ledger.d.ts +108 -0
  71. package/lib/client-types/work-output-eval.d.ts +15 -0
  72. package/lib/client-types/worklog-ledger.d.ts +88 -0
  73. package/lib/client-types/worklog-snapshot.d.ts +33 -0
  74. package/lib/client.js +5463 -0
  75. package/lib/config.d.ts +56 -0
  76. package/lib/config.js +115 -0
  77. package/lib/cron.d.ts +9 -0
  78. package/lib/cron.js +127 -0
  79. package/lib/domain.d.ts +819 -0
  80. package/lib/domain.js +662 -0
  81. package/lib/frame-admission.d.ts +6 -0
  82. package/lib/frame-admission.js +39 -0
  83. package/lib/frame-reference.d.ts +5 -0
  84. package/lib/frame-reference.js +12 -0
  85. package/lib/home-archive.d.ts +17 -0
  86. package/lib/home-archive.js +177 -0
  87. package/lib/home-directory.d.ts +6 -0
  88. package/lib/home-directory.js +38 -0
  89. package/lib/home-space.d.ts +50 -0
  90. package/lib/home-space.js +120 -0
  91. package/lib/home-store.d.ts +126 -0
  92. package/lib/home-store.js +837 -0
  93. package/lib/host.d.ts +22 -0
  94. package/lib/host.js +15 -0
  95. package/lib/human-inbox-transport.d.ts +23 -0
  96. package/lib/human-inbox-transport.js +105 -0
  97. package/lib/human-notification-runner.d.ts +83 -0
  98. package/lib/human-notification-runner.js +138 -0
  99. package/lib/human-notification-webhook.d.ts +18 -0
  100. package/lib/human-notification-webhook.js +102 -0
  101. package/lib/human-turn-completion.d.ts +61 -0
  102. package/lib/human-turn-completion.js +157 -0
  103. package/lib/inbox-return-notifier.d.ts +46 -0
  104. package/lib/inbox-return-notifier.js +279 -0
  105. package/lib/inbox-stalled-escalation-runner.d.ts +31 -0
  106. package/lib/inbox-stalled-escalation-runner.js +68 -0
  107. package/lib/inbox-stalled-topic.d.ts +16 -0
  108. package/lib/inbox-stalled-topic.js +44 -0
  109. package/lib/inbox-wake-policy.d.ts +19 -0
  110. package/lib/inbox-wake-policy.js +28 -0
  111. package/lib/inbox-wake-runner.d.ts +45 -0
  112. package/lib/inbox-wake-runner.js +481 -0
  113. package/lib/index.d.ts +29 -0
  114. package/lib/index.js +480 -0
  115. package/lib/known-format.d.ts +3 -0
  116. package/lib/known-format.js +8 -0
  117. package/lib/learning-scheduler.d.ts +37 -0
  118. package/lib/learning-scheduler.js +182 -0
  119. package/lib/learning-session-runner.d.ts +50 -0
  120. package/lib/learning-session-runner.js +435 -0
  121. package/lib/learning-trigger.d.ts +18 -0
  122. package/lib/learning-trigger.js +92 -0
  123. package/lib/learning-usage.d.ts +18 -0
  124. package/lib/learning-usage.js +49 -0
  125. package/lib/ledger.d.ts +191 -0
  126. package/lib/ledger.js +752 -0
  127. package/lib/local-human-host.d.ts +11 -0
  128. package/lib/local-human-host.js +191 -0
  129. package/lib/local-human-token.d.ts +9 -0
  130. package/lib/local-human-token.js +32 -0
  131. package/lib/local-owner.d.ts +8 -0
  132. package/lib/local-owner.js +26 -0
  133. package/lib/message-source.d.ts +10 -0
  134. package/lib/message-source.js +2 -0
  135. package/lib/organization-location.d.ts +20 -0
  136. package/lib/organization-location.js +80 -0
  137. package/lib/organization-workspace.d.ts +22 -0
  138. package/lib/organization-workspace.js +32 -0
  139. package/lib/project-ledger.d.ts +183 -0
  140. package/lib/project-ledger.js +189 -0
  141. package/lib/project-review-transport.d.ts +23 -0
  142. package/lib/project-review-transport.js +155 -0
  143. package/lib/project-seat-context.d.ts +18 -0
  144. package/lib/project-seat-context.js +34 -0
  145. package/lib/proposal-review-transport.d.ts +26 -0
  146. package/lib/proposal-review-transport.js +104 -0
  147. package/lib/route-guard.d.ts +31 -0
  148. package/lib/route-guard.js +80 -0
  149. package/lib/safe-file.d.ts +24 -0
  150. package/lib/safe-file.js +93 -0
  151. package/lib/sample-organization.d.ts +10 -0
  152. package/lib/sample-organization.js +46 -0
  153. package/lib/service.d.ts +597 -0
  154. package/lib/service.js +4076 -0
  155. package/lib/session-surface.d.ts +11 -0
  156. package/lib/session-surface.js +109 -0
  157. package/lib/shared-knowledge-review-transport.d.ts +28 -0
  158. package/lib/shared-knowledge-review-transport.js +85 -0
  159. package/lib/shared-knowledge.d.ts +117 -0
  160. package/lib/shared-knowledge.js +240 -0
  161. package/lib/tools.d.ts +7 -0
  162. package/lib/tools.js +955 -0
  163. package/lib/ui-contract.d.ts +277 -0
  164. package/lib/ui-contract.js +12 -0
  165. package/lib/ui-host.d.ts +24 -0
  166. package/lib/ui-host.js +471 -0
  167. package/lib/untrusted-text.d.ts +16 -0
  168. package/lib/untrusted-text.js +50 -0
  169. package/lib/work-ledger.d.ts +109 -0
  170. package/lib/work-ledger.js +107 -0
  171. package/lib/work-output-eval.d.ts +16 -0
  172. package/lib/work-output-eval.js +58 -0
  173. package/lib/work-runner.d.ts +47 -0
  174. package/lib/work-runner.js +352 -0
  175. package/lib/worklog-ledger.d.ts +89 -0
  176. package/lib/worklog-ledger.js +91 -0
  177. package/lib/worklog-snapshot.d.ts +34 -0
  178. package/lib/worklog-snapshot.js +100 -0
  179. package/lib/writer-guard.d.ts +11 -0
  180. package/lib/writer-guard.js +207 -0
  181. package/package.json +172 -4
package/lib/domain.js ADDED
@@ -0,0 +1,662 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { z } from 'zod';
3
+ export const HOME_TYPES = ['personal', 'line', 'business', 'role', 'capability'];
4
+ export const ORGANIZATION_RELATIONS = ['member-of', 'fulfills-role', 'serves', 'belongs-to-line'];
5
+ export const ASSET_KINDS = ['memory', 'insight', 'knowledge', 'method', 'identity'];
6
+ export const ASSET_STATUSES = ['candidate', 'active', 'superseded', 'retired'];
7
+ export const HomeAddressSchema = z.string().refine((value) => {
8
+ const separator = value.indexOf(':');
9
+ if (separator <= 0 || separator === value.length - 1)
10
+ return false;
11
+ return HOME_TYPES.includes(value.slice(0, separator))
12
+ && /^[a-z0-9][a-z0-9._/-]*$/i.test(value.slice(separator + 1));
13
+ }, 'expected <personal|line|business|role|capability>:<id>');
14
+ export const UserAddressSchema = z.string().regex(/^user:[a-z0-9][a-z0-9._-]{0,127}$/i);
15
+ export const ProjectSeatAddressSchema = z.string().regex(/^seat:[a-z0-9][a-z0-9._-]{0,127}:seat-[a-f0-9]{20}$/);
16
+ export const InboxAddressSchema = z.union([HomeAddressSchema, UserAddressSchema, ProjectSeatAddressSchema]);
17
+ export const InboxSenderAddressSchema = z.union([InboxAddressSchema, z.literal('engine:inbox')]);
18
+ export const HumanActionSchema = z.strictObject({
19
+ request: z.string().trim().min(1).max(3000),
20
+ reason: z.string().trim().min(1).max(3000),
21
+ recommendation: z.string().trim().min(1).max(3000).optional(),
22
+ href: z.string().max(2000).optional(),
23
+ label: z.string().trim().min(1).max(80).optional(),
24
+ }).superRefine((action, context) => {
25
+ if (action.href === undefined)
26
+ return;
27
+ try {
28
+ const decoded = decodeURIComponent(action.href);
29
+ if (!decoded.startsWith('/') || decoded.startsWith('//') || /[\\\s\u0000-\u001f]/u.test(decoded)) {
30
+ throw new Error('unsafe path');
31
+ }
32
+ const url = new URL(action.href, 'https://bizagent.invalid');
33
+ if (url.origin !== 'https://bizagent.invalid' || url.pathname.startsWith('/api/')) {
34
+ throw new Error('unsafe path');
35
+ }
36
+ }
37
+ catch {
38
+ context.addIssue({ code: 'custom', path: ['href'], message: 'human action href must be an application page path' });
39
+ }
40
+ });
41
+ export const EvidenceRefSchema = z.discriminatedUnion('type', [
42
+ z.object({
43
+ type: z.literal('session-events'),
44
+ sessionId: z.string().min(1),
45
+ fromSeq: z.number().int().nonnegative(),
46
+ toSeq: z.number().int().nonnegative(),
47
+ }),
48
+ z.object({
49
+ type: z.literal('worklog'),
50
+ sessionId: z.string().min(1),
51
+ revisionId: z.string().min(1),
52
+ fingerprint: z.string().regex(/^[a-f0-9]{64}$/),
53
+ }),
54
+ z.object({
55
+ type: z.literal('tool-result'),
56
+ sessionId: z.string().min(1),
57
+ eventSeq: z.number().int().nonnegative(),
58
+ toolCallId: z.string().min(1),
59
+ }),
60
+ z.object({
61
+ type: z.literal('artifact'),
62
+ uri: z.string().min(1),
63
+ digest: z.string().min(1),
64
+ }),
65
+ z.object({
66
+ type: z.literal('document'),
67
+ uri: z.string().min(1),
68
+ digest: z.string().min(1),
69
+ authority: z.string().min(1).optional(),
70
+ }),
71
+ z.object({
72
+ type: z.literal('asset'),
73
+ ownerAddress: HomeAddressSchema,
74
+ assetId: z.string().min(1),
75
+ revision: z.number().int().positive(),
76
+ }),
77
+ z.object({
78
+ type: z.literal('user-confirmation'),
79
+ sessionId: z.string().min(1),
80
+ eventSeq: z.number().int().nonnegative(),
81
+ }),
82
+ ]);
83
+ export const AgentHomeSchema = z.object({
84
+ schemaVersion: z.literal(1),
85
+ id: z.string().min(1),
86
+ address: HomeAddressSchema,
87
+ type: z.enum(HOME_TYPES),
88
+ displayName: z.string().min(1),
89
+ owner: z.string().min(1).optional(),
90
+ revision: z.number().int().positive(),
91
+ contextDigest: z.string(),
92
+ status: z.enum(['active', 'archived']),
93
+ createdAt: z.string().min(1),
94
+ updatedAt: z.string().min(1),
95
+ });
96
+ export const OrganizationLinkSchema = z.object({
97
+ from: HomeAddressSchema,
98
+ to: HomeAddressSchema,
99
+ relation: z.enum(ORGANIZATION_RELATIONS),
100
+ });
101
+ export const OrganizationSchema = z.object({
102
+ schemaVersion: z.literal(1),
103
+ id: z.string().regex(/^[a-z0-9][a-z0-9._-]{0,63}$/),
104
+ name: z.string().min(1),
105
+ mission: z.string().min(1),
106
+ lineHome: HomeAddressSchema.optional(),
107
+ businessHome: HomeAddressSchema,
108
+ members: z.array(z.object({
109
+ personalHome: HomeAddressSchema,
110
+ roleHome: HomeAddressSchema,
111
+ })).min(1),
112
+ capabilityHomes: z.array(HomeAddressSchema),
113
+ links: z.array(OrganizationLinkSchema),
114
+ revision: z.number().int().positive(),
115
+ createdAt: z.string().min(1),
116
+ updatedAt: z.string().min(1),
117
+ }).superRefine((organization, context) => {
118
+ if (!organization.businessHome.startsWith('business:')) {
119
+ context.addIssue({ code: 'custom', path: ['businessHome'], message: 'businessHome must be a Business Home' });
120
+ }
121
+ if (organization.lineHome !== undefined && !organization.lineHome.startsWith('line:')) {
122
+ context.addIssue({ code: 'custom', path: ['lineHome'], message: 'lineHome must be a Line Home' });
123
+ }
124
+ const declared = new Set([organization.businessHome,
125
+ ...(organization.lineHome === undefined ? [] : [organization.lineHome])]);
126
+ const seenMembers = new Set();
127
+ organization.members.forEach((member, index) => {
128
+ if (!member.personalHome.startsWith('personal:')) {
129
+ context.addIssue({ code: 'custom', path: ['members', index, 'personalHome'], message: 'member must use a Personal Home' });
130
+ }
131
+ if (!member.roleHome.startsWith('role:')) {
132
+ context.addIssue({ code: 'custom', path: ['members', index, 'roleHome'], message: 'member role must use a Role Home' });
133
+ }
134
+ if (seenMembers.has(member.personalHome)) {
135
+ context.addIssue({ code: 'custom', path: ['members', index, 'personalHome'], message: 'duplicate organization member' });
136
+ }
137
+ seenMembers.add(member.personalHome);
138
+ declared.add(member.personalHome);
139
+ declared.add(member.roleHome);
140
+ });
141
+ organization.capabilityHomes.forEach((address, index) => {
142
+ if (!address.startsWith('capability:')) {
143
+ context.addIssue({ code: 'custom', path: ['capabilityHomes', index], message: 'capability must use a Capability Home' });
144
+ }
145
+ declared.add(address);
146
+ });
147
+ organization.links.forEach((link, index) => {
148
+ if (!declared.has(link.from) || !declared.has(link.to)) {
149
+ context.addIssue({ code: 'custom', path: ['links', index], message: 'link endpoints must belong to this organization' });
150
+ }
151
+ const validShape = link.relation === 'member-of'
152
+ ? link.from.startsWith('personal:') && link.to === organization.businessHome
153
+ : link.relation === 'fulfills-role'
154
+ ? link.from.startsWith('personal:') && link.to.startsWith('role:')
155
+ : link.relation === 'belongs-to-line'
156
+ ? organization.lineHome !== undefined && link.to === organization.lineHome
157
+ && (link.from.startsWith('business:') || link.from.startsWith('role:')
158
+ || link.from.startsWith('capability:'))
159
+ : (link.from.startsWith('role:') || link.from.startsWith('capability:'))
160
+ && link.to === organization.businessHome;
161
+ if (!validShape)
162
+ context.addIssue({ code: 'custom', path: ['links', index], message: 'relation has invalid Home types' });
163
+ });
164
+ });
165
+ export const AssetMetadataSchema = z.object({
166
+ schemaVersion: z.literal(1),
167
+ id: z.string().min(1),
168
+ ownerAddress: HomeAddressSchema,
169
+ kind: z.enum(ASSET_KINDS),
170
+ title: z.string().min(1).optional(),
171
+ description: z.string().min(1).max(1000),
172
+ tags: z.array(z.string().min(1).max(80)).default([]),
173
+ sourceRefs: z.array(EvidenceRefSchema).min(1),
174
+ confidence: z.number().min(0).max(1),
175
+ fitness: z.number().min(-1).max(1),
176
+ status: z.enum(ASSET_STATUSES),
177
+ revision: z.number().int().positive(),
178
+ proposalId: z.string().min(1).optional(),
179
+ proposalApplications: z.array(z.object({
180
+ proposalId: z.string().min(1), revision: z.number().int().positive(),
181
+ bodySha256: z.string().regex(/^[a-f0-9]{64}$/),
182
+ })).max(500).optional(),
183
+ createdAt: z.string().min(1),
184
+ updatedAt: z.string().min(1),
185
+ }).superRefine((asset, context) => {
186
+ const applications = asset.proposalApplications ?? [];
187
+ if (new Set(applications.map(item => item.proposalId)).size !== applications.length
188
+ || applications.some(item => item.revision > asset.revision)) {
189
+ context.addIssue({ code: 'custom', message: 'asset proposal applications conflict with revision history' });
190
+ }
191
+ });
192
+ export const SessionBindingSchema = z.object({
193
+ sessionId: z.string().min(1),
194
+ homeAddress: HomeAddressSchema,
195
+ cwd: z.string().optional(),
196
+ boundAt: z.string().min(1),
197
+ });
198
+ export const ProjectTurnParticipationSchema = z.object({
199
+ schemaVersion: z.literal(1),
200
+ projectId: z.string().min(1).max(128),
201
+ sessionId: z.string().min(1),
202
+ homeAddress: HomeAddressSchema,
203
+ turn: z.number().int().nonnegative(),
204
+ recordedAt: z.string().datetime(),
205
+ });
206
+ export const LearningMaterialObservationSchema = z.object({
207
+ schemaVersion: z.literal(1),
208
+ id: z.string().min(1),
209
+ homeAddress: HomeAddressSchema,
210
+ sessionId: z.string().min(1),
211
+ fingerprint: z.string().regex(/^[a-f0-9]{64}$/),
212
+ eventCount: z.number().int().positive(),
213
+ lastSeq: z.number().int().nonnegative(),
214
+ sourceKind: z.literal('worklog').optional(),
215
+ worklogRevisionId: z.string().min(1).optional(),
216
+ worklogRevision: z.number().int().positive().optional(),
217
+ observedAt: z.string().min(1),
218
+ }).superRefine((observation, context) => {
219
+ if (observation.sourceKind === 'worklog'
220
+ ? observation.worklogRevisionId === undefined || observation.worklogRevision === undefined
221
+ : observation.worklogRevisionId !== undefined || observation.worklogRevision !== undefined) {
222
+ context.addIssue({ code: 'custom', path: ['worklogRevisionId'],
223
+ message: 'Worklog material requires an exact revision ID and number' });
224
+ }
225
+ });
226
+ export const LearningMaterialReviewSchema = z.object({
227
+ schemaVersion: z.literal(1),
228
+ materialId: z.string().min(1),
229
+ homeAddress: HomeAddressSchema,
230
+ reviewerSessionId: z.string().min(1),
231
+ summary: z.string().min(1).max(2000),
232
+ batchRunId: z.string().min(1).optional(),
233
+ assetIds: z.array(z.string().min(1)).max(3).optional(),
234
+ reviewedAt: z.string().min(1),
235
+ });
236
+ export const LearningLibraryFileSchema = z.object({
237
+ path: z.string().min(1).max(500),
238
+ sha256: z.string().regex(/^[a-f0-9]{64}$/),
239
+ bytes: z.number().int().nonnegative().max(8 * 1024 * 1024),
240
+ });
241
+ export const LearningLibraryChangeSchema = z.object({
242
+ path: z.string().min(1).max(500),
243
+ result: z.enum(['created', 'updated', 'deleted']),
244
+ before: LearningLibraryFileSchema.optional(),
245
+ after: LearningLibraryFileSchema.optional(),
246
+ }).superRefine((change, context) => {
247
+ if (change.before?.path !== undefined && change.before.path !== change.path
248
+ || change.after?.path !== undefined && change.after.path !== change.path
249
+ || change.result === 'created' && (change.before !== undefined || change.after === undefined)
250
+ || change.result === 'deleted' && (change.before === undefined || change.after !== undefined)
251
+ || change.result === 'updated' && (change.before === undefined || change.after === undefined
252
+ || change.before.sha256 === change.after.sha256)) {
253
+ context.addIssue({ code: 'custom', path: ['result'], message: 'invalid learning library change' });
254
+ }
255
+ });
256
+ export const LearningBatchRunSchema = z.object({
257
+ schemaVersion: z.literal(1),
258
+ id: z.string().min(1),
259
+ homeAddress: HomeAddressSchema,
260
+ reviewerSessionId: z.string().min(1),
261
+ materialIds: z.array(z.string().min(1)).min(1).max(8),
262
+ status: z.enum(['open', 'completed', 'abandoned']),
263
+ summary: z.string().min(1).max(2000).optional(),
264
+ libraryBefore: z.array(LearningLibraryFileSchema).max(1000).optional(),
265
+ libraryChanges: z.array(LearningLibraryChangeSchema).max(1000).optional(),
266
+ libraryChangeError: z.string().min(1).max(500).optional(),
267
+ createdAt: z.string().min(1),
268
+ updatedAt: z.string().min(1),
269
+ }).superRefine((run, context) => {
270
+ if (new Set(run.materialIds).size !== run.materialIds.length) {
271
+ context.addIssue({ code: 'custom', path: ['materialIds'], message: 'duplicate learning material' });
272
+ }
273
+ if (run.status !== 'open' && run.summary === undefined) {
274
+ context.addIssue({ code: 'custom', path: ['summary'], message: 'settled learning batch requires a summary' });
275
+ }
276
+ if (new Set((run.libraryBefore ?? []).map(file => file.path)).size !== (run.libraryBefore?.length ?? 0)
277
+ || new Set((run.libraryChanges ?? []).map(change => change.path)).size !== (run.libraryChanges?.length ?? 0)
278
+ || (run.status === 'open' && run.libraryChanges !== undefined)
279
+ || (run.status === 'open' && run.libraryChangeError !== undefined)
280
+ || (run.libraryChangeError !== undefined && run.libraryBefore === undefined)
281
+ || (run.libraryChanges !== undefined && (run.libraryBefore === undefined
282
+ || run.libraryChangeError !== undefined))) {
283
+ context.addIssue({ code: 'custom', path: ['libraryChanges'], message: 'invalid learning library snapshot chain' });
284
+ }
285
+ });
286
+ export const LearningLaunchSchema = z.object({
287
+ schemaVersion: z.literal(1),
288
+ id: z.string().min(1),
289
+ homeAddress: HomeAddressSchema,
290
+ requestedBySessionId: z.string().min(1),
291
+ workerSessionId: z.string().min(1),
292
+ workspace: z.string().min(1),
293
+ promptMessageId: z.string().min(1),
294
+ provider: z.string().min(1).max(200).optional(),
295
+ model: z.string().min(1).max(200).optional(),
296
+ maxTokens: z.number().int().positive().optional(),
297
+ trigger: z.enum(['explicit', 'schedule']).optional(),
298
+ scheduledFor: z.number().int().nonnegative().optional(),
299
+ status: z.enum(['reserved', 'active', 'completed', 'needs_review', 'abandoned']),
300
+ reason: z.string().min(1).max(2000).optional(),
301
+ createdAt: z.string().min(1),
302
+ updatedAt: z.string().min(1),
303
+ }).superRefine((launch, context) => {
304
+ if (launch.trigger === 'schedule' && launch.scheduledFor === undefined) {
305
+ context.addIssue({ code: 'custom', path: ['scheduledFor'], message: 'scheduled learning launch needs its due time' });
306
+ }
307
+ if (launch.status === 'needs_review' && !launch.reason) {
308
+ context.addIssue({ code: 'custom', path: ['reason'], message: 'uncertain learning launch needs a reason' });
309
+ }
310
+ });
311
+ export const LearningScheduleCursorSchema = z.object({
312
+ schemaVersion: z.literal(1),
313
+ homeAddress: HomeAddressSchema,
314
+ scheduleFingerprint: z.string().min(1),
315
+ nextDueAt: z.number().int().nonnegative(),
316
+ lastDueAt: z.number().int().nonnegative().optional(),
317
+ lastOutcome: z.enum(['launched', 'empty', 'limit', 'deferred', 'expired']).optional(),
318
+ lastLaunchId: z.string().min(1).optional(),
319
+ lastReason: z.string().min(1).max(2000).optional(),
320
+ updatedAt: z.string().min(1),
321
+ });
322
+ export const LearningSchedulePolicySchema = z.object({
323
+ schemaVersion: z.literal(1),
324
+ homeAddress: HomeAddressSchema,
325
+ revision: z.number().int().positive(),
326
+ enabled: z.boolean(),
327
+ cron: z.string().min(1).max(100),
328
+ dailyRuns: z.number().int().min(1).max(4),
329
+ updatedBy: z.literal('local-ui'),
330
+ updatedAt: z.string().min(1),
331
+ });
332
+ export const FRAME_MODES = ['lead', 'contribute', 'reference'];
333
+ export const AgentFrameSchema = z.object({
334
+ address: HomeAddressSchema,
335
+ mode: z.enum(FRAME_MODES),
336
+ revision: z.number().int().positive(),
337
+ digest: z.string(),
338
+ contextEpoch: z.number().int().positive().optional(),
339
+ openedAt: z.string().min(1),
340
+ updatedAt: z.string().min(1),
341
+ });
342
+ export const AgentFrameStateSchema = z.object({
343
+ schemaVersion: z.literal(1),
344
+ sessionId: z.string().min(1),
345
+ primaryHome: HomeAddressSchema,
346
+ contextEpoch: z.number().int().positive().optional(),
347
+ lastCompactionSeq: z.number().int().nonnegative().optional(),
348
+ frames: z.array(AgentFrameSchema).max(8),
349
+ focusAddress: HomeAddressSchema.optional(),
350
+ updatedAt: z.string().min(1),
351
+ }).superRefine((state, context) => {
352
+ const addresses = new Set();
353
+ let leads = 0;
354
+ state.frames.forEach((frame, index) => {
355
+ if (frame.address === state.primaryHome || addresses.has(frame.address)) {
356
+ context.addIssue({ code: 'custom', path: ['frames', index, 'address'], message: 'frame address must be unique and different from the primary Home' });
357
+ }
358
+ addresses.add(frame.address);
359
+ if (frame.mode === 'lead')
360
+ leads += 1;
361
+ });
362
+ if (leads > 1)
363
+ context.addIssue({ code: 'custom', path: ['frames'], message: 'only one lead Frame is allowed' });
364
+ if (state.focusAddress !== undefined && !addresses.has(state.focusAddress)) {
365
+ context.addIssue({ code: 'custom', path: ['focusAddress'], message: 'focus must name an active Frame' });
366
+ }
367
+ });
368
+ export const HumanTurnProvenanceSchema = z.strictObject({
369
+ sessionId: z.string().min(1),
370
+ turn: z.number().int().nonnegative(),
371
+ fromSeq: z.number().int().nonnegative(),
372
+ userMessageSeq: z.number().int().nonnegative(),
373
+ toSeq: z.number().int().nonnegative(),
374
+ sourceSha256: z.string().regex(/^[a-f0-9]{64}$/),
375
+ recipient: UserAddressSchema,
376
+ authorityRef: z.string().min(1).max(500),
377
+ summary: z.string().trim().min(1).max(160).regex(/^[^\r\n\u0000-\u001f]+$/u).optional(),
378
+ }).refine(value => value.fromSeq < value.userMessageSeq && value.userMessageSeq < value.toSeq, 'human turn source sequence is invalid');
379
+ export const InboxMessageSchema = z.object({
380
+ schemaVersion: z.literal(1),
381
+ id: z.string().min(1),
382
+ idempotencyKey: z.string().min(1).max(128),
383
+ fromAddress: InboxSenderAddressSchema,
384
+ returnToSessionId: z.string().min(1).optional(),
385
+ /** Frozen from the sender so an automatic wake can run after host recovery. */
386
+ wakeOptions: z.object({
387
+ provider: z.string().min(1).optional(),
388
+ model: z.string().min(1).optional(),
389
+ maxTokens: z.number().int().positive().optional(),
390
+ }).optional(),
391
+ returnAddress: z.union([HomeAddressSchema, ProjectSeatAddressSchema]).optional(),
392
+ projectId: z.string().min(1).max(128).optional(),
393
+ to: z.array(InboxAddressSchema).min(1).max(8),
394
+ cc: z.array(InboxAddressSchema).max(8),
395
+ subject: z.string().min(1).max(200),
396
+ body: z.string().min(1).max(16000),
397
+ kind: z.enum(['ordinary', 'project-outcome-rework', 'conclusion', 'escalation', 'work-request', 'work-result', 'work-notice',
398
+ 'turn-complete']),
399
+ priority: z.enum(['normal', 'urgent']),
400
+ due: z.string().min(1).optional(),
401
+ contextRefs: z.array(z.string().min(1).max(500)).max(16),
402
+ humanAction: HumanActionSchema.optional(),
403
+ humanTurn: HumanTurnProvenanceSchema.optional(),
404
+ projectOutcomeRework: z.strictObject({
405
+ projectId: z.string().min(1), claimId: z.string().min(1), lead: HomeAddressSchema,
406
+ }).optional(),
407
+ automaticEscalation: z.object({
408
+ sourceMessageId: z.string().min(1),
409
+ authorityRef: z.string().min(1).max(500),
410
+ }).optional(),
411
+ conclusionArtifact: z.string().min(1).max(500).optional(),
412
+ replyTo: z.string().min(1).optional(),
413
+ rootId: z.string().min(1),
414
+ chain: z.number().int().min(0).max(7),
415
+ topicId: z.string().min(1).optional(),
416
+ hop: z.number().int().min(0).max(7).optional(),
417
+ sentAt: z.string().min(1),
418
+ }).superRefine((message, context) => {
419
+ const seats = [message.fromAddress, ...message.to, ...message.cc, message.returnAddress]
420
+ .filter((address) => address !== undefined && isProjectSeatAddress(address));
421
+ if (seats.some(address => message.projectId === undefined
422
+ || !address.startsWith(`seat:${message.projectId}:`))) {
423
+ context.addIssue({ code: 'custom', path: ['projectId'], message: 'Inbox Seat addresses must belong to the recorded Project' });
424
+ }
425
+ if (message.humanTurn && (message.kind !== 'turn-complete'
426
+ || message.to.length !== 1 || message.to[0] !== message.humanTurn.recipient
427
+ || message.cc.length !== 0 || !message.humanAction?.href
428
+ || message.priority !== 'normal' || message.contextRefs.length !== 0
429
+ || message.due !== undefined || message.replyTo !== undefined
430
+ || message.returnToSessionId !== message.humanTurn.sessionId
431
+ || message.returnAddress !== message.fromAddress)) {
432
+ context.addIssue({ code: 'custom', path: ['humanTurn'],
433
+ message: 'human turn completion needs one recipient and its source Session' });
434
+ }
435
+ if (message.kind === 'turn-complete' && !message.humanTurn) {
436
+ context.addIssue({ code: 'custom', path: ['humanTurn'],
437
+ message: 'turn completion needs durable Session provenance' });
438
+ }
439
+ if (message.projectOutcomeRework && (message.kind !== 'project-outcome-rework'
440
+ || message.fromAddress !== 'engine:inbox'
441
+ || message.projectId !== message.projectOutcomeRework.projectId
442
+ || message.id !== stableId('project-outcome-rework', `${message.projectOutcomeRework.projectId}:${message.projectOutcomeRework.claimId}`)
443
+ || message.to.length !== 1 || message.to[0] !== message.projectOutcomeRework.lead
444
+ || message.cc.length !== 0 || message.priority !== 'urgent')) {
445
+ context.addIssue({ code: 'custom', path: ['projectOutcomeRework'],
446
+ message: 'Project outcome rework notice must target its lead Home with stable provenance' });
447
+ }
448
+ if (message.kind === 'project-outcome-rework' && !message.projectOutcomeRework) {
449
+ context.addIssue({ code: 'custom', path: ['projectOutcomeRework'],
450
+ message: 'Project outcome rework notice needs durable Project provenance' });
451
+ }
452
+ });
453
+ export function inboxShouldConverge(hop, topicCount) {
454
+ return hop >= 4 || topicCount >= 9;
455
+ }
456
+ export const InboxWakeOriginSchema = z.object({
457
+ schemaVersion: z.literal(1),
458
+ sessionId: z.string().min(1),
459
+ homeAddress: HomeAddressSchema,
460
+ topicId: z.string().min(1),
461
+ hop: z.number().int().min(0).max(6),
462
+ wakePlanId: z.string().min(1),
463
+ createdAt: z.string().min(1),
464
+ });
465
+ export const InboxWakePlanSchema = z.object({
466
+ schemaVersion: z.literal(1),
467
+ id: z.string().min(1),
468
+ recipient: z.union([HomeAddressSchema, ProjectSeatAddressSchema]),
469
+ messageIds: z.array(z.string().min(1)).min(1).max(20),
470
+ sessionId: z.string().min(1),
471
+ topicId: z.string().min(1),
472
+ scope: z.enum(['topic', 'owner']).optional(),
473
+ reason: z.enum(['initial', 'nudge-due', 'nudge-stale']).optional(),
474
+ hop: z.number().int().min(0).max(6),
475
+ workspace: z.string().min(1),
476
+ dueAt: z.string().min(1),
477
+ createdAt: z.string().min(1),
478
+ dispatchedAt: z.string().min(1).optional(),
479
+ /** Why the plan was retired without starting a Session; its letters stay in the normal Inbox. */
480
+ retiredReason: z.string().min(1).max(500).optional(),
481
+ });
482
+ export const InboxReturnRouteSchema = z.object({
483
+ schemaVersion: z.literal(1),
484
+ messageId: z.string().min(1),
485
+ recipient: z.union([HomeAddressSchema, ProjectSeatAddressSchema]),
486
+ sessionId: z.string().min(1),
487
+ status: z.enum(['queued', 'fallback']),
488
+ updatedAt: z.string().min(1),
489
+ reason: z.string().min(1).optional(),
490
+ });
491
+ export const InboxDeliverySchema = z.object({
492
+ schemaVersion: z.literal(1),
493
+ messageId: z.string().min(1),
494
+ recipient: InboxAddressSchema,
495
+ status: z.enum(['delivered', 'bounced']),
496
+ reason: z.string().min(1).optional(),
497
+ wakePolicy: z.enum(['immediate', 'batch', 'scheduled', 'carried']).optional(),
498
+ wakeAt: z.string().min(1).optional(),
499
+ nudgedAt: z.string().min(1).optional(),
500
+ nudgeReason: z.enum(['due', 'stale']).optional(),
501
+ handledAt: z.string().min(1).optional(),
502
+ handledBySessionId: z.string().min(1).optional(),
503
+ handledByUserId: z.string().min(1).optional(),
504
+ updatedAt: z.string().min(1),
505
+ });
506
+ export const HumanNotificationPreferenceSchema = z.strictObject({
507
+ schemaVersion: z.literal(1), recipient: UserAddressSchema,
508
+ enabled: z.boolean(), revision: z.number().int().positive(),
509
+ updatedAt: z.string().datetime(),
510
+ });
511
+ /** A push attempt is unknown before calling an external channel, so crashes cannot cause blind resend. */
512
+ export const InboxHumanNotificationSchema = z.object({
513
+ schemaVersion: z.literal(1),
514
+ id: z.string().min(1),
515
+ messageId: z.string().min(1),
516
+ recipient: UserAddressSchema,
517
+ attempt: z.number().int().nonnegative().default(1),
518
+ priorRejections: z.array(z.object({
519
+ id: z.string().min(1),
520
+ attemptedAt: z.string().min(1),
521
+ resolvedAt: z.string().min(1),
522
+ reason: z.string().min(1),
523
+ })).default([]),
524
+ status: z.enum(['unknown', 'sent', 'rejected', 'suppressed']),
525
+ attemptedAt: z.string().min(1).optional(),
526
+ suppressedAt: z.string().datetime().optional(),
527
+ resolvedAt: z.string().min(1).optional(),
528
+ providerReceipt: z.string().min(1).optional(),
529
+ reason: z.string().min(1).optional(),
530
+ }).superRefine((notice, context) => {
531
+ if (notice.status === 'suppressed') {
532
+ if (notice.attempt !== 0 || notice.attemptedAt || !notice.suppressedAt
533
+ || notice.resolvedAt !== notice.suppressedAt || !notice.reason?.trim()
534
+ || notice.providerReceipt || notice.priorRejections.length > 0) {
535
+ context.addIssue({ code: 'custom', message: 'suppressed notice cannot have an external attempt' });
536
+ }
537
+ return;
538
+ }
539
+ if (notice.attempt < 1 || !notice.attemptedAt || notice.suppressedAt) {
540
+ context.addIssue({ code: 'custom', message: 'channel notice needs an actual attempt' });
541
+ }
542
+ if (notice.status === 'sent' && !notice.providerReceipt) {
543
+ context.addIssue({ code: 'custom', path: ['providerReceipt'], message: 'sent requires a provider receipt' });
544
+ }
545
+ if (notice.status === 'rejected' && !notice.reason) {
546
+ context.addIssue({ code: 'custom', path: ['reason'], message: 'rejected requires a reason' });
547
+ }
548
+ });
549
+ export const MemoryProposalSchema = z.object({
550
+ id: z.string().min(1),
551
+ fromAddress: HomeAddressSchema,
552
+ toAddress: HomeAddressSchema,
553
+ proposedKind: z.enum(ASSET_KINDS),
554
+ description: z.string().min(1).max(1000),
555
+ body: z.string().min(1),
556
+ tags: z.array(z.string().min(1).max(80)).default([]),
557
+ sourceRefs: z.array(EvidenceRefSchema).min(1),
558
+ status: z.enum(['pending', 'accepted', 'rejected', 'withdrawn']),
559
+ baseAsset: z.object({ id: z.string().min(1), revision: z.number().int().positive(),
560
+ bodySha256: z.string().regex(/^[a-f0-9]{64}$/) }).optional(),
561
+ targetAssetId: z.string().min(1).optional(),
562
+ appliedAssetRevision: z.number().int().positive().optional(),
563
+ appliedBodySha256: z.string().regex(/^[a-f0-9]{64}$/).optional(),
564
+ humanReview: z.object({
565
+ reviewerId: z.string().trim().min(1),
566
+ authorityRef: z.string().trim().min(1),
567
+ action: z.enum(['accept', 'reject']),
568
+ decision: z.string().trim().min(1),
569
+ reviewedAt: z.string().min(1),
570
+ }).optional(),
571
+ decision: z.string().min(1).optional(),
572
+ createdAt: z.string().min(1),
573
+ decidedAt: z.string().min(1).optional(),
574
+ }).superRefine((proposal, context) => {
575
+ if ((proposal.appliedAssetRevision === undefined) !== (proposal.appliedBodySha256 === undefined)) {
576
+ context.addIssue({ code: 'custom', message: 'proposal application revision and digest must appear together' });
577
+ }
578
+ if (proposal.baseAsset && proposal.status === 'accepted'
579
+ && (proposal.targetAssetId !== proposal.baseAsset.id || proposal.appliedAssetRevision === undefined)) {
580
+ context.addIssue({ code: 'custom', message: 'accepted replacement proposal needs a matching applied asset' });
581
+ }
582
+ if (proposal.humanReview && proposal.status === 'pending' && proposal.humanReview.action !== 'accept') {
583
+ context.addIssue({ code: 'custom', message: 'pending proposal can only reserve human acceptance' });
584
+ }
585
+ if (proposal.humanReview && proposal.status !== 'pending'
586
+ && ((proposal.status === 'accepted' ? 'accept' : 'reject') !== proposal.humanReview.action
587
+ || proposal.decision !== proposal.humanReview.decision
588
+ || proposal.decidedAt !== proposal.humanReview.reviewedAt)) {
589
+ context.addIssue({ code: 'custom', message: 'proposal decision must match its human review' });
590
+ }
591
+ });
592
+ export const FITNESS_SIGNALS = ['retrieved', 'applied', 'confirmed', 'contradicted', 'failed'];
593
+ export const FitnessReceiptSchema = z.object({
594
+ id: z.string().min(1),
595
+ assetId: z.string().min(1),
596
+ ownerAddress: HomeAddressSchema,
597
+ sessionId: z.string().min(1),
598
+ eventSeq: z.number().int().nonnegative().optional(),
599
+ assetRevision: z.number().int().positive().optional(),
600
+ assetBodySha256: z.string().regex(/^[a-f0-9]{64}$/).optional(),
601
+ signal: z.enum(FITNESS_SIGNALS),
602
+ outcome: z.string().min(1).optional(),
603
+ createdAt: z.string().min(1),
604
+ }).superRefine((receipt, context) => {
605
+ if ((receipt.assetRevision === undefined) !== (receipt.assetBodySha256 === undefined)) {
606
+ context.addIssue({ code: 'custom', path: ['assetBodySha256'],
607
+ message: 'fitness receipt asset version and body digest must appear together' });
608
+ }
609
+ });
610
+ export const LEARNING_CHECKPOINT_STATUSES = ['pending', 'remembered', 'skipped', 'failed'];
611
+ export const LearningCheckpointSchema = z.object({
612
+ schemaVersion: z.literal(1),
613
+ id: z.string().min(1),
614
+ sessionId: z.string().min(1),
615
+ turn: z.number().int().nonnegative(),
616
+ homeAddress: HomeAddressSchema,
617
+ reason: z.literal('explicit-correction'),
618
+ status: z.enum(LEARNING_CHECKPOINT_STATUSES),
619
+ evidenceRef: EvidenceRefSchema,
620
+ excerpt: z.string().min(1).max(4000),
621
+ assetId: z.string().min(1).optional(),
622
+ decision: z.string().min(1).optional(),
623
+ createdAt: z.string().min(1),
624
+ settledAt: z.string().min(1).optional(),
625
+ });
626
+ export function parseHomeAddress(value) {
627
+ return HomeAddressSchema.parse(value);
628
+ }
629
+ export function parseInboxAddress(value) {
630
+ return InboxAddressSchema.parse(value);
631
+ }
632
+ export function isUserAddress(value) {
633
+ return UserAddressSchema.safeParse(value).success;
634
+ }
635
+ export function isHomeAddress(value) {
636
+ return HomeAddressSchema.safeParse(value).success;
637
+ }
638
+ export function isProjectSeatAddress(value) {
639
+ return ProjectSeatAddressSchema.safeParse(value).success;
640
+ }
641
+ export function parseUserAddress(value) {
642
+ return UserAddressSchema.parse(value);
643
+ }
644
+ export function homeTypeOf(address) {
645
+ return address.slice(0, address.indexOf(':'));
646
+ }
647
+ export function stableId(prefix, value) {
648
+ return `${prefix}-${createHash('sha256').update(value).digest('hex').slice(0, 20)}`;
649
+ }
650
+ export function safeHomeId(address) {
651
+ const readable = address
652
+ .toLowerCase()
653
+ .replace(':', '--')
654
+ .replace(/[^a-z0-9._-]+/g, '-')
655
+ .replace(/^-+|-+$/g, '')
656
+ .slice(0, 64);
657
+ return `${readable}-${createHash('sha256').update(address).digest('hex').slice(0, 8)}`;
658
+ }
659
+ export function nowIso() {
660
+ return new Date().toISOString();
661
+ }
662
+ //# sourceMappingURL=domain.js.map
@@ -0,0 +1,6 @@
1
+ import type { Agent, PreStepDecision } from '@deepseek-ai/dsh-agent';
2
+ import type { BizAgentService } from './service.js';
3
+ import './message-source.js';
4
+ /** Resolve selected references at DSH's admission boundary, before the model sees the step. */
5
+ export declare function admitFrameReferences(service: BizAgentService, agent: Agent, decision: PreStepDecision, signal: AbortSignal): Promise<PreStepDecision>;
6
+ //# sourceMappingURL=frame-admission.d.ts.map