@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
@@ -0,0 +1,11 @@
1
+ import type { SessionEvent } from '@deepseek-ai/dsh-session';
2
+ import type { BizAgentService } from './service.js';
3
+ import type { UiAgentRuntime, UiSessionActivity, UiSessionContext, UiSessionSummary } from './ui-contract.js';
4
+ type Runtime = (sessionId: string) => UiAgentRuntime;
5
+ /** Lightweight sidebar data: no transcript reads and no creation of new Home bindings. */
6
+ export declare function sessionCatalog(service: BizAgentService, runtime: Runtime): UiSessionSummary[];
7
+ export declare function sessionContext(service: BizAgentService, sessionId: string, cwd: string | undefined, events: readonly SessionEvent[], runtime: Runtime): Promise<UiSessionContext>;
8
+ /** Project successful durable results only; a tool call alone is never evidence of success. */
9
+ export declare function sessionActivity(events: readonly SessionEvent[]): UiSessionActivity[];
10
+ export {};
11
+ //# sourceMappingURL=session-surface.d.ts.map
@@ -0,0 +1,109 @@
1
+ import { principalSessionId, projectMainSessionId } from './agent-session-address.js';
2
+ /** Lightweight sidebar data: no transcript reads and no creation of new Home bindings. */
3
+ export function sessionCatalog(service, runtime) {
4
+ const works = service.workLedger?.list() ?? [];
5
+ const projects = service.projectLedger?.list() ?? [];
6
+ const learning = new Set(service.ledger.listLearningLaunches().map(item => item.workerSessionId));
7
+ const mains = new Map(projects.map(project => [projectMainSessionId(project.id), project]));
8
+ return service.ledger.listBindings().filter(binding => service.store.hasHome(binding.homeAddress)).map(binding => {
9
+ const home = service.store.getHome(binding.homeAddress);
10
+ const work = works.find(item => item.workerSessionId === binding.sessionId && item.assignee === home.address);
11
+ const main = mains.get(binding.sessionId);
12
+ const project = main ?? projects.find(item => item.id === work?.projectId);
13
+ return {
14
+ sessionId: binding.sessionId,
15
+ home: { address: home.address, displayName: home.displayName,
16
+ scope: service.store.isPersonalHome(home.address) ? 'personal' : 'organization' },
17
+ runtime: runtime(binding.sessionId),
18
+ kind: work ? 'work' : main ? 'project-main' : learning.has(binding.sessionId) ? 'learning'
19
+ : service.ledger.getInboxWakeOrigin(binding.sessionId) ? 'inbox'
20
+ : principalSessionId(home.address) === binding.sessionId ? 'principal' : 'conversation',
21
+ ...(work ? { workId: work.id, workGoal: work.goal, workStatus: work.status } : {}),
22
+ ...(project ? { projectId: project.id, projectTitle: project.title } : {}),
23
+ };
24
+ });
25
+ }
26
+ export async function sessionContext(service, sessionId, cwd, events, runtime) {
27
+ const frames = await service.frameStatusForSession(sessionId, cwd, events);
28
+ const summary = sessionCatalog(service, runtime).find(item => item.sessionId === sessionId);
29
+ if (!summary)
30
+ throw new Error('Session has no available Home');
31
+ const assets = service.store.listAssets(summary.home.address);
32
+ const work = (service.workLedger?.list() ?? []).filter(item => item.requester === summary.home.address && item.returnToSessionId === sessionId
33
+ || item.assignee === summary.home.address && item.workerSessionId === sessionId)
34
+ .sort((a, b) => b.updatedAt.localeCompare(a.updatedAt) || a.id.localeCompare(b.id));
35
+ const ids = new Set(work.flatMap(item => item.projectId ? [item.projectId] : []));
36
+ for (const participation of service.ledger.listProjectTurns()) {
37
+ if (participation.sessionId === sessionId)
38
+ ids.add(participation.projectId);
39
+ }
40
+ const projects = (service.projectLedger?.list() ?? []).filter(project => (project.lead === summary.home.address || project.seats?.some(seat => seat.homeAddress === summary.home.address && seat.status === 'active'))
41
+ && (ids.has(project.id) || project.createdBySessionId === sessionId || summary.projectId === project.id));
42
+ return {
43
+ summary,
44
+ frames: frames.state.frames.map(frame => ({ address: frame.address,
45
+ displayName: service.store.hasHome(frame.address) ? service.store.getHome(frame.address).displayName : frame.address, mode: frame.mode,
46
+ focused: frames.state.focusAddress === frame.address, refreshRequired: frames.staleFrames.includes(frame.address) })),
47
+ home: { activeAssets: assets.filter(asset => asset.status === 'active').length,
48
+ candidates: assets.filter(asset => asset.status === 'candidate').length,
49
+ pendingProposals: service.ledger.listProposals({ toAddress: summary.home.address, status: 'pending' }).length },
50
+ work: work.slice(0, 20).map(item => ({ id: item.id, goal: item.goal, requester: item.requester,
51
+ assignee: item.assignee, status: item.status, updatedAt: item.updatedAt,
52
+ requesterName: service.store.hasHome(item.requester) ? service.store.getHome(item.requester).displayName : item.requester,
53
+ assigneeName: service.store.hasHome(item.assignee) ? service.store.getHome(item.assignee).displayName : item.assignee,
54
+ returnToSessionId: item.returnToSessionId,
55
+ runtime: item.workerSessionId ? runtime(item.workerSessionId) : 'unloaded',
56
+ ...(item.workerSessionId ? { workerSessionId: item.workerSessionId } : {}),
57
+ ...(item.projectId ? { projectId: item.projectId } : {}),
58
+ ...(item.resultSubject ? { resultSubject: item.resultSubject } : {}),
59
+ ...(item.resultBody ? { resultBody: item.resultBody } : {}),
60
+ ...(item.resultArtifacts ? { resultArtifacts: item.resultArtifacts } : {}),
61
+ })),
62
+ projects: projects.slice(0, 20).map(project => ({ id: project.id, title: project.title,
63
+ status: project.status, goal: project.goal })),
64
+ activity: sessionActivity(events).slice(-100), moreWork: work.length > 20,
65
+ };
66
+ }
67
+ const ACTIONS = {
68
+ bizagent_memory_remember: 'memory', bizagent_memory_update: 'memory',
69
+ bizagent_learning_checkpoint: 'learning', bizagent_memory_propose: 'proposal',
70
+ bizagent_knowledge_propose: 'proposal', bizagent_work_dispatch: 'work',
71
+ bizagent_work_start: 'work', bizagent_work_accept: 'work', bizagent_work_complete: 'work',
72
+ bizagent_work_cancel: 'work', bizagent_project_create: 'project', bizagent_project_submit: 'project',
73
+ bizagent_project_review: 'project', bizagent_frame_open: 'frame', bizagent_learning_launch: 'learning',
74
+ bizagent_learning_batch_settle: 'learning', bizagent_worklog_record: 'learning',
75
+ };
76
+ /** Project successful durable results only; a tool call alone is never evidence of success. */
77
+ export function sessionActivity(events) {
78
+ const calls = new Map();
79
+ const activity = [];
80
+ for (const event of events) {
81
+ if (event.type === 'tool/call' && ACTIONS[event.data.name]) {
82
+ calls.set(String(event.data.callId), { action: event.data.name, kind: ACTIONS[event.data.name], turn: event.data.turn });
83
+ }
84
+ if (event.type !== 'tool/result' || event.data.error !== undefined || event.data.message.isError)
85
+ continue;
86
+ const call = calls.get(String(event.data.message.toolCallId));
87
+ if (!call)
88
+ continue;
89
+ calls.delete(String(event.data.message.toolCallId));
90
+ const text = event.data.message.content.filter(block => block.type === 'text').map(block => block.text).join('\n');
91
+ if (text.length > 256 * 1024)
92
+ continue;
93
+ try {
94
+ const value = JSON.parse(text);
95
+ if (!value || typeof value !== 'object' || value.error)
96
+ continue;
97
+ const entity = [value.asset, value.work, value.project, value.proposal, value.checkpoint]
98
+ .find(item => item && typeof item === 'object');
99
+ const title = entity?.title ?? entity?.description ?? entity?.goal ?? entity?.reason;
100
+ activity.push({ id: `activity-${event.seq}`, seq: event.seq, turn: call.turn, kind: call.kind,
101
+ action: call.action, title: typeof title === 'string' ? title.slice(0, 300) : '',
102
+ ...(typeof entity?.status === 'string' ? { status: entity.status } : {}),
103
+ ...(typeof entity?.id === 'string' ? { entityId: entity.id } : {}) });
104
+ }
105
+ catch { /* Foreign or malformed results stay available in DSH's original tool detail. */ }
106
+ }
107
+ return activity;
108
+ }
109
+ //# sourceMappingURL=session-surface.js.map
@@ -0,0 +1,28 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import type { BizAgentService } from './service.js';
3
+ import type { SharedKnowledgeProposal } from './shared-knowledge.js';
4
+ export declare const SHARED_KNOWLEDGE_REVIEW_API = "/api/bizagent/shared-knowledge/decision";
5
+ export interface SharedKnowledgeReviewOptions {
6
+ authenticateHuman(request: IncomingMessage): {
7
+ userId: string;
8
+ } | null | Promise<{
9
+ userId: string;
10
+ } | null>;
11
+ authorizeMutation(request: IncomingMessage, principal: {
12
+ userId: string;
13
+ }): boolean | Promise<boolean>;
14
+ /** The host must check line.admin or platform.admin for this exact proposal. */
15
+ authorizeProposal(request: IncomingMessage, principal: {
16
+ userId: string;
17
+ }, proposal: SharedKnowledgeProposal, action: 'accept' | 'reject'): string | null | Promise<string | null>;
18
+ }
19
+ export declare function createSharedKnowledgeReviewHttpHandler(service: BizAgentService, options: SharedKnowledgeReviewOptions): (request: IncomingMessage, response: ServerResponse) => Promise<void>;
20
+ /** Mount on the local DSH web server behind the loopback Host / same-origin route guard. */
21
+ export declare function registerSharedKnowledgeReviewRoute(webServer: {
22
+ register: (route: {
23
+ kind: 'exact';
24
+ path: string;
25
+ handler: (request: IncomingMessage, response: ServerResponse) => Promise<void>;
26
+ }) => () => void;
27
+ }, service: BizAgentService, options: SharedKnowledgeReviewOptions): () => void;
28
+ //# sourceMappingURL=shared-knowledge-review-transport.d.ts.map
@@ -0,0 +1,85 @@
1
+ import { z } from 'zod';
2
+ import { guardLocalRoute } from './route-guard.js';
3
+ export const SHARED_KNOWLEDGE_REVIEW_API = '/api/bizagent/shared-knowledge/decision';
4
+ const Decision = z.strictObject({
5
+ proposalId: z.string().min(1).max(200),
6
+ action: z.enum(['accept', 'reject']),
7
+ decision: z.string().trim().min(1).max(4000),
8
+ });
9
+ export function createSharedKnowledgeReviewHttpHandler(service, options) {
10
+ return async (request, response) => {
11
+ response.setHeader('Cache-Control', 'no-store');
12
+ response.setHeader('Content-Type', 'application/json; charset=utf-8');
13
+ response.setHeader('X-Content-Type-Options', 'nosniff');
14
+ const reply = (code, value) => {
15
+ response.statusCode = code;
16
+ response.end(JSON.stringify(value));
17
+ };
18
+ if (request.method !== 'GET' && request.method !== 'POST') {
19
+ response.setHeader('Allow', 'GET, POST');
20
+ reply(405, { error: 'method-not-allowed' });
21
+ return;
22
+ }
23
+ try {
24
+ const principal = await options.authenticateHuman(request);
25
+ if (!principal?.userId.trim()) {
26
+ reply(401, { error: 'authentication-required' });
27
+ return;
28
+ }
29
+ if (request.method === 'GET') {
30
+ const proposals = service.ledger.listSharedKnowledgeProposals();
31
+ const visible = [];
32
+ for (const proposal of proposals) {
33
+ const accept = !!(await options.authorizeProposal(request, principal, proposal, 'accept'))?.trim();
34
+ const reject = !!(await options.authorizeProposal(request, principal, proposal, 'reject'))?.trim();
35
+ if (accept || reject)
36
+ visible.push({ proposal, actions: { accept, reject } });
37
+ }
38
+ reply(200, { count: visible.length, proposals: visible });
39
+ return;
40
+ }
41
+ if (!await options.authorizeMutation(request, principal)) {
42
+ reply(403, { error: 'mutation-forbidden' });
43
+ return;
44
+ }
45
+ if (!String(request.headers['content-type'] ?? '').toLowerCase().startsWith('application/json')) {
46
+ reply(415, { error: 'content-type' });
47
+ return;
48
+ }
49
+ const parts = [];
50
+ let bytes = 0;
51
+ for await (const chunk of request) {
52
+ const part = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
53
+ bytes += part.length;
54
+ if (bytes > 16 * 1024) {
55
+ reply(413, { error: 'body-too-large' });
56
+ return;
57
+ }
58
+ parts.push(part);
59
+ }
60
+ const input = Decision.parse(JSON.parse(Buffer.concat(parts).toString('utf8')));
61
+ const proposal = service.ledger.getSharedKnowledgeProposal(input.proposalId);
62
+ if (!proposal) {
63
+ reply(404, { error: 'proposal-not-found' });
64
+ return;
65
+ }
66
+ const authorityRef = await options.authorizeProposal(request, principal, proposal, input.action);
67
+ if (!authorityRef?.trim()) {
68
+ reply(403, { error: 'proposal-forbidden' });
69
+ return;
70
+ }
71
+ reply(200, await service.decideSharedKnowledgeAsHuman({ ...input,
72
+ reviewerId: principal.userId, authorityRef }));
73
+ }
74
+ catch (error) {
75
+ reply(error instanceof z.ZodError || error instanceof SyntaxError ? 400 : 409, { error: error instanceof z.ZodError || error instanceof SyntaxError
76
+ ? 'invalid-request' : 'shared-knowledge-conflict' });
77
+ }
78
+ };
79
+ }
80
+ /** Mount on the local DSH web server behind the loopback Host / same-origin route guard. */
81
+ export function registerSharedKnowledgeReviewRoute(webServer, service, options) {
82
+ return webServer.register({ kind: 'exact', path: SHARED_KNOWLEDGE_REVIEW_API,
83
+ handler: guardLocalRoute(createSharedKnowledgeReviewHttpHandler(service, options)) });
84
+ }
85
+ //# sourceMappingURL=shared-knowledge-review-transport.js.map
@@ -0,0 +1,117 @@
1
+ import { z } from 'zod';
2
+ declare const DocumentSchema: z.ZodObject<{
3
+ schemaVersion: z.ZodLiteral<1>;
4
+ id: z.ZodString;
5
+ scope: z.ZodEnum<{
6
+ line: "line";
7
+ common: "common";
8
+ }>;
9
+ line: z.ZodOptional<z.ZodString>;
10
+ description: z.ZodString;
11
+ proposalId: z.ZodString;
12
+ reviewerId: z.ZodString;
13
+ authorityRef: z.ZodString;
14
+ publishedAt: z.ZodString;
15
+ bodySha256: z.ZodString;
16
+ }, z.core.$strict>;
17
+ export type SharedKnowledgeTarget = {
18
+ scope: 'line';
19
+ line: string;
20
+ } | {
21
+ scope: 'common';
22
+ };
23
+ export type SharedKnowledgeDocument = z.infer<typeof DocumentSchema> & {
24
+ body: string;
25
+ path: string;
26
+ };
27
+ export type PublishSharedKnowledgeInput = SharedKnowledgeTarget & {
28
+ id: string;
29
+ description: string;
30
+ body: string;
31
+ proposalId: string;
32
+ reviewerId: string;
33
+ authorityRef: string;
34
+ publishedAt: string;
35
+ };
36
+ export declare const SharedKnowledgeProposalSchema: z.ZodObject<{
37
+ id: z.ZodString;
38
+ fromAddress: z.ZodString;
39
+ target: z.ZodDiscriminatedUnion<[z.ZodObject<{
40
+ scope: z.ZodLiteral<"line">;
41
+ line: z.ZodString;
42
+ }, z.core.$strict>, z.ZodObject<{
43
+ scope: z.ZodLiteral<"common">;
44
+ }, z.core.$strict>], "scope">;
45
+ description: z.ZodString;
46
+ body: z.ZodString;
47
+ sourceRefs: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
48
+ type: z.ZodLiteral<"session-events">;
49
+ sessionId: z.ZodString;
50
+ fromSeq: z.ZodNumber;
51
+ toSeq: z.ZodNumber;
52
+ }, z.core.$strip>, z.ZodObject<{
53
+ type: z.ZodLiteral<"worklog">;
54
+ sessionId: z.ZodString;
55
+ revisionId: z.ZodString;
56
+ fingerprint: z.ZodString;
57
+ }, z.core.$strip>, z.ZodObject<{
58
+ type: z.ZodLiteral<"tool-result">;
59
+ sessionId: z.ZodString;
60
+ eventSeq: z.ZodNumber;
61
+ toolCallId: z.ZodString;
62
+ }, z.core.$strip>, z.ZodObject<{
63
+ type: z.ZodLiteral<"artifact">;
64
+ uri: z.ZodString;
65
+ digest: z.ZodString;
66
+ }, z.core.$strip>, z.ZodObject<{
67
+ type: z.ZodLiteral<"document">;
68
+ uri: z.ZodString;
69
+ digest: z.ZodString;
70
+ authority: z.ZodOptional<z.ZodString>;
71
+ }, z.core.$strip>, z.ZodObject<{
72
+ type: z.ZodLiteral<"asset">;
73
+ ownerAddress: z.ZodString;
74
+ assetId: z.ZodString;
75
+ revision: z.ZodNumber;
76
+ }, z.core.$strip>, z.ZodObject<{
77
+ type: z.ZodLiteral<"user-confirmation">;
78
+ sessionId: z.ZodString;
79
+ eventSeq: z.ZodNumber;
80
+ }, z.core.$strip>], "type">>;
81
+ status: z.ZodEnum<{
82
+ rejected: "rejected";
83
+ pending: "pending";
84
+ accepted: "accepted";
85
+ }>;
86
+ humanReview: z.ZodOptional<z.ZodObject<{
87
+ reviewerId: z.ZodString;
88
+ authorityRef: z.ZodString;
89
+ action: z.ZodEnum<{
90
+ accept: "accept";
91
+ reject: "reject";
92
+ }>;
93
+ decision: z.ZodString;
94
+ reviewedAt: z.ZodString;
95
+ }, z.core.$strict>>;
96
+ targetDocumentId: z.ZodOptional<z.ZodString>;
97
+ createdAt: z.ZodString;
98
+ decidedAt: z.ZodOptional<z.ZodString>;
99
+ }, z.core.$strict>;
100
+ export type SharedKnowledgeProposal = z.infer<typeof SharedKnowledgeProposalSchema>;
101
+ /** File-backed shared sources. PMO Agent Home knowledge stays in its separate Home directory. */
102
+ export declare class SharedKnowledgeStore {
103
+ readonly root: string;
104
+ constructor(root: string);
105
+ pathFor(target: SharedKnowledgeTarget): string;
106
+ publish(input: PublishSharedKnowledgeInput): Promise<SharedKnowledgeDocument>;
107
+ read(target: SharedKnowledgeTarget, idInput: string): Promise<SharedKnowledgeDocument | undefined>;
108
+ /**
109
+ * Search the shared layers. A document that fails its safety or integrity checks is skipped and
110
+ * reported through `onSkip`, so one hand-edited file cannot break retrieval for every Home.
111
+ */
112
+ search(targets: readonly SharedKnowledgeTarget[], query?: string, onSkip?: (path: string, error: unknown) => void): Promise<SharedKnowledgeDocument[]>;
113
+ private ensureDirectory;
114
+ private safeExistingDirectory;
115
+ }
116
+ export {};
117
+ //# sourceMappingURL=shared-knowledge.d.ts.map
@@ -0,0 +1,240 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { link, lstat, mkdir, readFile, readdir, realpath, rm, writeFile } from 'node:fs/promises';
3
+ import { isAbsolute, join, relative, resolve } from 'node:path';
4
+ import YAML from 'yaml';
5
+ import { z } from 'zod';
6
+ import { EvidenceRefSchema, HomeAddressSchema } from './domain.js';
7
+ const LineId = z.string().regex(/^[a-z0-9][a-z0-9._-]{0,127}$/i);
8
+ const KnowledgeId = z.string().regex(/^knowledge-[a-f0-9]{20}$/);
9
+ const DocumentSchema = z.strictObject({
10
+ schemaVersion: z.literal(1),
11
+ id: KnowledgeId,
12
+ scope: z.enum(['line', 'common']),
13
+ line: LineId.optional(),
14
+ description: z.string().trim().min(1).max(1000),
15
+ proposalId: z.string().min(1),
16
+ reviewerId: z.string().trim().min(1),
17
+ authorityRef: z.string().trim().min(1),
18
+ publishedAt: z.string().datetime(),
19
+ bodySha256: z.string().regex(/^[a-f0-9]{64}$/),
20
+ }).superRefine((value, context) => {
21
+ if ((value.scope === 'line') !== (value.line !== undefined)) {
22
+ context.addIssue({ code: 'custom', path: ['line'], message: 'line scope requires one line ID' });
23
+ }
24
+ });
25
+ export const SharedKnowledgeProposalSchema = z.strictObject({
26
+ id: z.string().regex(/^proposal-[a-f0-9]{20}$/),
27
+ fromAddress: HomeAddressSchema,
28
+ target: z.discriminatedUnion('scope', [
29
+ z.strictObject({ scope: z.literal('line'), line: LineId }),
30
+ z.strictObject({ scope: z.literal('common') }),
31
+ ]),
32
+ description: z.string().trim().min(1).max(1000),
33
+ body: z.string().trim().min(1).max(128 * 1024),
34
+ sourceRefs: z.array(EvidenceRefSchema).min(1),
35
+ status: z.enum(['pending', 'accepted', 'rejected']),
36
+ humanReview: z.strictObject({
37
+ reviewerId: z.string().trim().min(1),
38
+ authorityRef: z.string().trim().min(1),
39
+ action: z.enum(['accept', 'reject']),
40
+ decision: z.string().trim().min(1),
41
+ reviewedAt: z.string().datetime(),
42
+ }).optional(),
43
+ targetDocumentId: KnowledgeId.optional(),
44
+ createdAt: z.string().datetime(),
45
+ decidedAt: z.string().datetime().optional(),
46
+ }).superRefine((proposal, context) => {
47
+ if (proposal.status === 'accepted' && (!proposal.targetDocumentId || proposal.humanReview?.action !== 'accept')) {
48
+ context.addIssue({ code: 'custom', message: 'accepted shared Knowledge needs a reviewed document' });
49
+ }
50
+ if (proposal.status === 'rejected' && (proposal.targetDocumentId || proposal.humanReview?.action !== 'reject')) {
51
+ context.addIssue({ code: 'custom', message: 'rejected shared Knowledge needs a human rejection' });
52
+ }
53
+ if (proposal.humanReview && proposal.status === 'pending' && proposal.humanReview.action !== 'accept') {
54
+ context.addIssue({ code: 'custom', message: 'pending shared Knowledge can only reserve acceptance' });
55
+ }
56
+ if (proposal.status !== 'pending' && proposal.decidedAt !== proposal.humanReview?.reviewedAt) {
57
+ context.addIssue({ code: 'custom', message: 'shared Knowledge decision time must match human review' });
58
+ }
59
+ });
60
+ /** File-backed shared sources. PMO Agent Home knowledge stays in its separate Home directory. */
61
+ export class SharedKnowledgeStore {
62
+ root;
63
+ constructor(root) { this.root = resolve(root); }
64
+ pathFor(target) {
65
+ return target.scope === 'common'
66
+ ? join(this.root, 'knowledge', 'common')
67
+ : join(this.root, 'lines', LineId.parse(target.line), 'knowledge');
68
+ }
69
+ async publish(input) {
70
+ const id = KnowledgeId.parse(input.id);
71
+ const body = input.body.trim();
72
+ if (!body || Buffer.byteLength(body) > 128 * 1024) {
73
+ throw new Error('shared Knowledge body must contain 1 to 131072 bytes');
74
+ }
75
+ const metadata = DocumentSchema.parse({
76
+ schemaVersion: 1, id, scope: input.scope,
77
+ ...(input.scope === 'line' ? { line: input.line } : {}),
78
+ description: input.description, proposalId: input.proposalId,
79
+ reviewerId: input.reviewerId, authorityRef: input.authorityRef,
80
+ publishedAt: input.publishedAt,
81
+ bodySha256: createHash('sha256').update(body).digest('hex'),
82
+ });
83
+ const directory = await this.ensureDirectory(input);
84
+ const path = join(directory, `${id}.md`);
85
+ const existing = await this.read(input, id);
86
+ if (existing !== undefined) {
87
+ if (existing.proposalId !== metadata.proposalId || existing.bodySha256 !== metadata.bodySha256
88
+ || existing.description !== metadata.description || existing.reviewerId !== metadata.reviewerId
89
+ || existing.authorityRef !== metadata.authorityRef) {
90
+ throw new Error(`shared Knowledge document conflicts with existing publication: ${id}`);
91
+ }
92
+ return existing;
93
+ }
94
+ const content = `---\n${YAML.stringify(metadata, { lineWidth: 0 })}---\n${body}\n`;
95
+ const temporary = join(directory, `.${id}.${process.pid}.${randomUUID()}.tmp`);
96
+ await writeFile(temporary, content, { flag: 'wx', mode: 0o600 });
97
+ try {
98
+ await link(temporary, path);
99
+ }
100
+ catch (error) {
101
+ if (!isExists(error))
102
+ throw error;
103
+ const raced = await this.read(input, id);
104
+ if (raced?.proposalId !== metadata.proposalId || raced.bodySha256 !== metadata.bodySha256) {
105
+ throw new Error(`shared Knowledge document conflicts with existing publication: ${id}`);
106
+ }
107
+ return raced;
108
+ }
109
+ finally {
110
+ await rm(temporary, { force: true });
111
+ }
112
+ return { ...metadata, body, path };
113
+ }
114
+ async read(target, idInput) {
115
+ const id = KnowledgeId.parse(idInput);
116
+ if (!await this.safeExistingDirectory(target))
117
+ return undefined;
118
+ const path = join(this.pathFor(target), `${id}.md`);
119
+ let info;
120
+ try {
121
+ info = await lstat(path);
122
+ }
123
+ catch (error) {
124
+ if (isMissing(error))
125
+ return undefined;
126
+ throw error;
127
+ }
128
+ if (!info.isFile() || info.isSymbolicLink() || info.size > 160 * 1024) {
129
+ throw new Error(`unsafe shared Knowledge document: ${path}`);
130
+ }
131
+ const content = await readFile(path, 'utf8');
132
+ const end = content.indexOf('\n---\n', 4);
133
+ if (!content.startsWith('---\n') || end < 0)
134
+ throw new Error(`malformed shared Knowledge document: ${path}`);
135
+ const metadata = DocumentSchema.parse(YAML.parse(content.slice(4, end)));
136
+ const body = content.slice(end + 5).trim();
137
+ if (metadata.id !== id || metadata.scope !== target.scope
138
+ || (target.scope === 'line' && metadata.line !== LineId.parse(target.line))
139
+ || createHash('sha256').update(body).digest('hex') !== metadata.bodySha256) {
140
+ throw new Error(`shared Knowledge document integrity mismatch: ${path}`);
141
+ }
142
+ return { ...metadata, body, path };
143
+ }
144
+ /**
145
+ * Search the shared layers. A document that fails its safety or integrity checks is skipped and
146
+ * reported through `onSkip`, so one hand-edited file cannot break retrieval for every Home.
147
+ */
148
+ async search(targets, query = '', onSkip) {
149
+ const terms = query.trim().toLowerCase();
150
+ const results = [];
151
+ for (const target of targets) {
152
+ if (!await this.safeExistingDirectory(target))
153
+ continue;
154
+ const directory = this.pathFor(target);
155
+ let entries;
156
+ try {
157
+ entries = await readdir(directory, { withFileTypes: true });
158
+ }
159
+ catch (error) {
160
+ if (isMissing(error))
161
+ continue;
162
+ throw error;
163
+ }
164
+ for (const entry of entries) {
165
+ if (!entry.isFile() || !/^knowledge-[a-f0-9]{20}\.md$/.test(entry.name))
166
+ continue;
167
+ let document;
168
+ try {
169
+ document = await this.read(target, entry.name.slice(0, -3));
170
+ }
171
+ catch (error) {
172
+ onSkip?.(join(directory, entry.name), error);
173
+ continue;
174
+ }
175
+ if (document && (!terms || `${document.description}\n${document.body}`.toLowerCase().includes(terms))) {
176
+ results.push(document);
177
+ }
178
+ }
179
+ }
180
+ return results.sort((a, b) => b.publishedAt.localeCompare(a.publishedAt) || a.id.localeCompare(b.id));
181
+ }
182
+ async ensureDirectory(target) {
183
+ if (!isAbsolute(this.root) || await realpath(this.root) !== this.root) {
184
+ throw new Error('shared Knowledge root must be a real absolute directory');
185
+ }
186
+ const segments = target.scope === 'common'
187
+ ? ['knowledge', 'common'] : ['lines', LineId.parse(target.line), 'knowledge'];
188
+ let current = this.root;
189
+ for (const segment of segments) {
190
+ current = join(current, segment);
191
+ try {
192
+ await mkdir(current);
193
+ }
194
+ catch (error) {
195
+ if (!isExists(error))
196
+ throw error;
197
+ }
198
+ const info = await lstat(current);
199
+ if (!info.isDirectory() || info.isSymbolicLink()) {
200
+ throw new Error(`shared Knowledge path must be a real directory: ${current}`);
201
+ }
202
+ const canonical = await realpath(current);
203
+ const rel = relative(this.root, canonical);
204
+ if (rel.startsWith('..') || isAbsolute(rel))
205
+ throw new Error('shared Knowledge path escapes its root');
206
+ }
207
+ return current;
208
+ }
209
+ async safeExistingDirectory(target) {
210
+ if (await realpath(this.root) !== this.root) {
211
+ throw new Error('shared Knowledge root must be a real directory');
212
+ }
213
+ const segments = target.scope === 'common'
214
+ ? ['knowledge', 'common'] : ['lines', LineId.parse(target.line), 'knowledge'];
215
+ let current = this.root;
216
+ for (const segment of segments) {
217
+ current = join(current, segment);
218
+ let info;
219
+ try {
220
+ info = await lstat(current);
221
+ }
222
+ catch (error) {
223
+ if (isMissing(error))
224
+ return false;
225
+ throw error;
226
+ }
227
+ if (!info.isDirectory() || info.isSymbolicLink()) {
228
+ throw new Error(`shared Knowledge path must be a real directory: ${current}`);
229
+ }
230
+ }
231
+ return true;
232
+ }
233
+ }
234
+ function isMissing(error) {
235
+ return error instanceof Error && 'code' in error && error.code === 'ENOENT';
236
+ }
237
+ function isExists(error) {
238
+ return error instanceof Error && 'code' in error && error.code === 'EEXIST';
239
+ }
240
+ //# sourceMappingURL=shared-knowledge.js.map
package/lib/tools.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { BizAgentService } from './service.js';
3
+ import type { InboxMessage } from './domain.js';
4
+ import type { WorkRunner } from './work-runner.js';
5
+ import type { LearningSessionRunner } from './learning-session-runner.js';
6
+ export declare function registerTools(ctx: Context, service: BizAgentService, workRunner: WorkRunner, learningRunner: LearningSessionRunner, onInboxSent?: (message: InboxMessage) => void): void;
7
+ //# sourceMappingURL=tools.d.ts.map