@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.
- package/CHANGELOG.md +124 -0
- package/LICENSE +21 -0
- package/README.md +61 -2
- package/cordis.patch.yml +14 -0
- package/docs/README.md +23 -0
- package/docs/architecture.md +154 -0
- package/docs/concepts.md +192 -0
- package/docs/configuration.md +154 -0
- package/docs/quickstart.md +103 -0
- package/docs/quickstart.zh-CN.md +86 -0
- package/docs/tools.md +607 -0
- package/docs/user-guide.zh-CN.md +132 -0
- package/docs/verification.md +69 -0
- package/examples/README.md +64 -0
- package/examples/demo-runtime.mjs +71 -0
- package/examples/learning-lab.mjs +263 -0
- package/examples/scenarios.mjs +45 -0
- package/lib/agent-preset.d.ts +20 -0
- package/lib/agent-preset.js +46 -0
- package/lib/agent-session-address.d.ts +14 -0
- package/lib/agent-session-address.js +15 -0
- package/lib/agent-session-coordinator.d.ts +24 -0
- package/lib/agent-session-coordinator.js +101 -0
- package/lib/cli.d.ts +3 -0
- package/lib/cli.js +142 -0
- package/lib/client-types/agent-session-address.d.ts +13 -0
- package/lib/client-types/client/agent-mentions.d.ts +3 -0
- package/lib/client-types/client/api.d.ts +180 -0
- package/lib/client-types/client/components.d.ts +22 -0
- package/lib/client-types/client/controller.d.ts +23 -0
- package/lib/client-types/client/frame-participants.d.ts +5 -0
- package/lib/client-types/client/home-name.d.ts +9 -0
- package/lib/client-types/client/human-inbox.d.ts +8 -0
- package/lib/client-types/client/index.d.ts +13 -0
- package/lib/client-types/client/locales.d.ts +796 -0
- package/lib/client-types/client/markdown.d.ts +12 -0
- package/lib/client-types/client/project-artifact-link.d.ts +10 -0
- package/lib/client-types/client/project-outcome-review-inbox.d.ts +13 -0
- package/lib/client-types/client/projects.d.ts +11 -0
- package/lib/client-types/client/sample-guide.d.ts +9 -0
- package/lib/client-types/client/session-resource.d.ts +34 -0
- package/lib/client-types/client/session-surfaces.d.ts +24 -0
- package/lib/client-types/client/styles.d.ts +1 -0
- package/lib/client-types/config.d.ts +55 -0
- package/lib/client-types/cron.d.ts +8 -0
- package/lib/client-types/domain.d.ts +818 -0
- package/lib/client-types/frame-reference.d.ts +4 -0
- package/lib/client-types/home-directory.d.ts +5 -0
- package/lib/client-types/home-space.d.ts +49 -0
- package/lib/client-types/home-store.d.ts +125 -0
- package/lib/client-types/inbox-stalled-topic.d.ts +15 -0
- package/lib/client-types/inbox-wake-policy.d.ts +18 -0
- package/lib/client-types/known-format.d.ts +2 -0
- package/lib/client-types/learning-scheduler.d.ts +36 -0
- package/lib/client-types/learning-session-runner.d.ts +49 -0
- package/lib/client-types/learning-trigger.d.ts +17 -0
- package/lib/client-types/learning-usage.d.ts +17 -0
- package/lib/client-types/ledger.d.ts +190 -0
- package/lib/client-types/local-owner.d.ts +7 -0
- package/lib/client-types/message-source.d.ts +9 -0
- package/lib/client-types/organization-location.d.ts +19 -0
- package/lib/client-types/project-ledger.d.ts +182 -0
- package/lib/client-types/project-seat-context.d.ts +17 -0
- package/lib/client-types/safe-file.d.ts +23 -0
- package/lib/client-types/sample-organization.d.ts +9 -0
- package/lib/client-types/service.d.ts +596 -0
- package/lib/client-types/shared-knowledge.d.ts +116 -0
- package/lib/client-types/ui-contract.d.ts +276 -0
- package/lib/client-types/untrusted-text.d.ts +15 -0
- package/lib/client-types/work-ledger.d.ts +108 -0
- package/lib/client-types/work-output-eval.d.ts +15 -0
- package/lib/client-types/worklog-ledger.d.ts +88 -0
- package/lib/client-types/worklog-snapshot.d.ts +33 -0
- package/lib/client.js +5463 -0
- package/lib/config.d.ts +56 -0
- package/lib/config.js +115 -0
- package/lib/cron.d.ts +9 -0
- package/lib/cron.js +127 -0
- package/lib/domain.d.ts +819 -0
- package/lib/domain.js +662 -0
- package/lib/frame-admission.d.ts +6 -0
- package/lib/frame-admission.js +39 -0
- package/lib/frame-reference.d.ts +5 -0
- package/lib/frame-reference.js +12 -0
- package/lib/home-archive.d.ts +17 -0
- package/lib/home-archive.js +177 -0
- package/lib/home-directory.d.ts +6 -0
- package/lib/home-directory.js +38 -0
- package/lib/home-space.d.ts +50 -0
- package/lib/home-space.js +120 -0
- package/lib/home-store.d.ts +126 -0
- package/lib/home-store.js +837 -0
- package/lib/host.d.ts +22 -0
- package/lib/host.js +15 -0
- package/lib/human-inbox-transport.d.ts +23 -0
- package/lib/human-inbox-transport.js +105 -0
- package/lib/human-notification-runner.d.ts +83 -0
- package/lib/human-notification-runner.js +138 -0
- package/lib/human-notification-webhook.d.ts +18 -0
- package/lib/human-notification-webhook.js +102 -0
- package/lib/human-turn-completion.d.ts +61 -0
- package/lib/human-turn-completion.js +157 -0
- package/lib/inbox-return-notifier.d.ts +46 -0
- package/lib/inbox-return-notifier.js +279 -0
- package/lib/inbox-stalled-escalation-runner.d.ts +31 -0
- package/lib/inbox-stalled-escalation-runner.js +68 -0
- package/lib/inbox-stalled-topic.d.ts +16 -0
- package/lib/inbox-stalled-topic.js +44 -0
- package/lib/inbox-wake-policy.d.ts +19 -0
- package/lib/inbox-wake-policy.js +28 -0
- package/lib/inbox-wake-runner.d.ts +45 -0
- package/lib/inbox-wake-runner.js +481 -0
- package/lib/index.d.ts +29 -0
- package/lib/index.js +480 -0
- package/lib/known-format.d.ts +3 -0
- package/lib/known-format.js +8 -0
- package/lib/learning-scheduler.d.ts +37 -0
- package/lib/learning-scheduler.js +182 -0
- package/lib/learning-session-runner.d.ts +50 -0
- package/lib/learning-session-runner.js +435 -0
- package/lib/learning-trigger.d.ts +18 -0
- package/lib/learning-trigger.js +92 -0
- package/lib/learning-usage.d.ts +18 -0
- package/lib/learning-usage.js +49 -0
- package/lib/ledger.d.ts +191 -0
- package/lib/ledger.js +752 -0
- package/lib/local-human-host.d.ts +11 -0
- package/lib/local-human-host.js +191 -0
- package/lib/local-human-token.d.ts +9 -0
- package/lib/local-human-token.js +32 -0
- package/lib/local-owner.d.ts +8 -0
- package/lib/local-owner.js +26 -0
- package/lib/message-source.d.ts +10 -0
- package/lib/message-source.js +2 -0
- package/lib/organization-location.d.ts +20 -0
- package/lib/organization-location.js +80 -0
- package/lib/organization-workspace.d.ts +22 -0
- package/lib/organization-workspace.js +32 -0
- package/lib/project-ledger.d.ts +183 -0
- package/lib/project-ledger.js +189 -0
- package/lib/project-review-transport.d.ts +23 -0
- package/lib/project-review-transport.js +155 -0
- package/lib/project-seat-context.d.ts +18 -0
- package/lib/project-seat-context.js +34 -0
- package/lib/proposal-review-transport.d.ts +26 -0
- package/lib/proposal-review-transport.js +104 -0
- package/lib/route-guard.d.ts +31 -0
- package/lib/route-guard.js +80 -0
- package/lib/safe-file.d.ts +24 -0
- package/lib/safe-file.js +93 -0
- package/lib/sample-organization.d.ts +10 -0
- package/lib/sample-organization.js +46 -0
- package/lib/service.d.ts +597 -0
- package/lib/service.js +4076 -0
- package/lib/session-surface.d.ts +11 -0
- package/lib/session-surface.js +109 -0
- package/lib/shared-knowledge-review-transport.d.ts +28 -0
- package/lib/shared-knowledge-review-transport.js +85 -0
- package/lib/shared-knowledge.d.ts +117 -0
- package/lib/shared-knowledge.js +240 -0
- package/lib/tools.d.ts +7 -0
- package/lib/tools.js +955 -0
- package/lib/ui-contract.d.ts +277 -0
- package/lib/ui-contract.js +12 -0
- package/lib/ui-host.d.ts +24 -0
- package/lib/ui-host.js +471 -0
- package/lib/untrusted-text.d.ts +16 -0
- package/lib/untrusted-text.js +50 -0
- package/lib/work-ledger.d.ts +109 -0
- package/lib/work-ledger.js +107 -0
- package/lib/work-output-eval.d.ts +16 -0
- package/lib/work-output-eval.js +58 -0
- package/lib/work-runner.d.ts +47 -0
- package/lib/work-runner.js +352 -0
- package/lib/worklog-ledger.d.ts +89 -0
- package/lib/worklog-ledger.js +91 -0
- package/lib/worklog-snapshot.d.ts +34 -0
- package/lib/worklog-snapshot.js +100 -0
- package/lib/writer-guard.d.ts +11 -0
- package/lib/writer-guard.js +207 -0
- package/package.json +172 -4
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { HomeAddressSchema } from './domain.js';
|
|
4
|
+
export const WorklogRevisionSchema = z.object({
|
|
5
|
+
schemaVersion: z.literal(1),
|
|
6
|
+
id: z.string().min(1),
|
|
7
|
+
sessionId: z.string().min(1),
|
|
8
|
+
homeAddress: HomeAddressSchema,
|
|
9
|
+
revision: z.number().int().positive(),
|
|
10
|
+
idempotencyKey: z.string().min(1).max(128),
|
|
11
|
+
description: z.string().min(1).max(240),
|
|
12
|
+
body: z.string().min(1).max(16000),
|
|
13
|
+
status: z.enum(['ongoing', 'completed', 'blocked', 'failed']),
|
|
14
|
+
origin: z.enum(['agent', 'work-result', 'session-activity']).optional(),
|
|
15
|
+
cwd: z.string().min(1).optional(),
|
|
16
|
+
evidence: z.object({
|
|
17
|
+
fromSeq: z.number().int().nonnegative(),
|
|
18
|
+
toSeq: z.number().int().nonnegative(),
|
|
19
|
+
fingerprint: z.string().regex(/^[a-f0-9]{64}$/),
|
|
20
|
+
}),
|
|
21
|
+
files: z.array(z.object({
|
|
22
|
+
path: z.string().min(1).max(500),
|
|
23
|
+
sha256: z.string().regex(/^[a-f0-9]{64}$/),
|
|
24
|
+
bytes: z.number().int().nonnegative().max(8 * 1024 * 1024),
|
|
25
|
+
snapshot: z.literal(true).optional(),
|
|
26
|
+
})).max(10),
|
|
27
|
+
projectId: z.string().min(1).optional(),
|
|
28
|
+
workId: z.string().min(1).optional(),
|
|
29
|
+
recordedAt: z.string().min(1),
|
|
30
|
+
}).superRefine((revision, context) => {
|
|
31
|
+
if (revision.evidence.fromSeq > revision.evidence.toSeq) {
|
|
32
|
+
context.addIssue({ code: 'custom', path: ['evidence'], message: 'Worklog evidence range is reversed' });
|
|
33
|
+
}
|
|
34
|
+
if (new Set(revision.files.map(file => file.path)).size !== revision.files.length) {
|
|
35
|
+
context.addIssue({ code: 'custom', path: ['files'], message: 'duplicate Worklog file reference' });
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
export const BIZAGENT_WORKLOG_DOMAIN = defineDomain({
|
|
39
|
+
name: 'bizagent_worklog',
|
|
40
|
+
version: 0,
|
|
41
|
+
tables: { revisions: domainTable(WorklogRevisionSchema) },
|
|
42
|
+
});
|
|
43
|
+
export class WorklogLedger {
|
|
44
|
+
domain;
|
|
45
|
+
mutationTail = Promise.resolve();
|
|
46
|
+
constructor(domain) {
|
|
47
|
+
this.domain = domain;
|
|
48
|
+
}
|
|
49
|
+
static async open(facility) {
|
|
50
|
+
return new WorklogLedger(await facility.open(BIZAGENT_WORKLOG_DOMAIN));
|
|
51
|
+
}
|
|
52
|
+
async withWrite(update) {
|
|
53
|
+
const task = this.mutationTail.catch(() => undefined).then(update);
|
|
54
|
+
this.mutationTail = task;
|
|
55
|
+
return task;
|
|
56
|
+
}
|
|
57
|
+
get(id) {
|
|
58
|
+
const record = this.domain.table('revisions').get(id);
|
|
59
|
+
return record === undefined ? undefined : WorklogRevisionSchema.parse(record);
|
|
60
|
+
}
|
|
61
|
+
listVersions(sessionId) {
|
|
62
|
+
return this.all().filter(item => item.sessionId === sessionId)
|
|
63
|
+
.sort((a, b) => a.revision - b.revision);
|
|
64
|
+
}
|
|
65
|
+
listLatest(homeAddress) {
|
|
66
|
+
const latest = new Map();
|
|
67
|
+
for (const item of this.all()) {
|
|
68
|
+
if (item.homeAddress !== homeAddress)
|
|
69
|
+
continue;
|
|
70
|
+
const current = latest.get(item.sessionId);
|
|
71
|
+
if (current === undefined || item.revision > current.revision)
|
|
72
|
+
latest.set(item.sessionId, item);
|
|
73
|
+
}
|
|
74
|
+
return [...latest.values()]
|
|
75
|
+
.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt) || b.sessionId.localeCompare(a.sessionId));
|
|
76
|
+
}
|
|
77
|
+
async put(revision) {
|
|
78
|
+
const parsed = WorklogRevisionSchema.parse(revision);
|
|
79
|
+
if (this.get(parsed.id) !== undefined)
|
|
80
|
+
throw new Error(`Worklog revision already exists: ${parsed.id}`);
|
|
81
|
+
await this.domain.table('revisions').put(parsed.id, parsed);
|
|
82
|
+
}
|
|
83
|
+
async close() {
|
|
84
|
+
await this.domain.close();
|
|
85
|
+
}
|
|
86
|
+
all() {
|
|
87
|
+
return [...this.domain.table('revisions').entries()]
|
|
88
|
+
.map(([, record]) => WorklogRevisionSchema.parse(record));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=worklog-ledger.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare const WORKLOG_SNAPSHOT_DIRECTORY = "worklog-snapshots/sha256";
|
|
2
|
+
export declare const PROJECT_SNAPSHOT_DIRECTORY = "project-snapshots/sha256";
|
|
3
|
+
export declare const WORK_SNAPSHOT_DIRECTORY = "work-snapshots/sha256";
|
|
4
|
+
export declare const LEARNING_SNAPSHOT_DIRECTORY = "learning-snapshots/sha256";
|
|
5
|
+
export declare const MAX_WORKLOG_FILE_BYTES: number;
|
|
6
|
+
export declare function worklogSnapshotPath(sha256: string): string;
|
|
7
|
+
export declare function projectSnapshotPath(sha256: string): string;
|
|
8
|
+
export declare function workSnapshotPath(sha256: string): string;
|
|
9
|
+
export declare function learningSnapshotPath(sha256: string): string;
|
|
10
|
+
/** Immutable objects live under the Home root, so Home archives carry them. */
|
|
11
|
+
declare class HomeSnapshotStore {
|
|
12
|
+
private readonly homeRoot;
|
|
13
|
+
private readonly directoryName;
|
|
14
|
+
constructor(homeRoot: string, directoryName: string);
|
|
15
|
+
retain(bytes: Uint8Array): Promise<{
|
|
16
|
+
sha256: string;
|
|
17
|
+
bytes: number;
|
|
18
|
+
}>;
|
|
19
|
+
read(sha256: string, expectedBytes?: number): Promise<Buffer>;
|
|
20
|
+
}
|
|
21
|
+
export declare class WorklogSnapshotStore extends HomeSnapshotStore {
|
|
22
|
+
constructor(homeRoot: string);
|
|
23
|
+
}
|
|
24
|
+
export declare class ProjectSnapshotStore extends HomeSnapshotStore {
|
|
25
|
+
constructor(homeRoot: string);
|
|
26
|
+
}
|
|
27
|
+
export declare class WorkSnapshotStore extends HomeSnapshotStore {
|
|
28
|
+
constructor(homeRoot: string);
|
|
29
|
+
}
|
|
30
|
+
export declare class LearningSnapshotStore extends HomeSnapshotStore {
|
|
31
|
+
constructor(homeRoot: string);
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=worklog-snapshot.d.ts.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { link, lstat, mkdir, readFile, realpath, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
export const WORKLOG_SNAPSHOT_DIRECTORY = 'worklog-snapshots/sha256';
|
|
5
|
+
export const PROJECT_SNAPSHOT_DIRECTORY = 'project-snapshots/sha256';
|
|
6
|
+
export const WORK_SNAPSHOT_DIRECTORY = 'work-snapshots/sha256';
|
|
7
|
+
export const LEARNING_SNAPSHOT_DIRECTORY = 'learning-snapshots/sha256';
|
|
8
|
+
export const MAX_WORKLOG_FILE_BYTES = 8 * 1024 * 1024;
|
|
9
|
+
const DIGEST = /^[a-f0-9]{64}$/;
|
|
10
|
+
export function worklogSnapshotPath(sha256) {
|
|
11
|
+
if (!DIGEST.test(sha256))
|
|
12
|
+
throw new Error('invalid Worklog snapshot SHA-256');
|
|
13
|
+
return `${WORKLOG_SNAPSHOT_DIRECTORY}/${sha256}`;
|
|
14
|
+
}
|
|
15
|
+
export function projectSnapshotPath(sha256) {
|
|
16
|
+
if (!DIGEST.test(sha256))
|
|
17
|
+
throw new Error('invalid Project snapshot SHA-256');
|
|
18
|
+
return `${PROJECT_SNAPSHOT_DIRECTORY}/${sha256}`;
|
|
19
|
+
}
|
|
20
|
+
export function workSnapshotPath(sha256) {
|
|
21
|
+
if (!DIGEST.test(sha256))
|
|
22
|
+
throw new Error('invalid Work snapshot SHA-256');
|
|
23
|
+
return `${WORK_SNAPSHOT_DIRECTORY}/${sha256}`;
|
|
24
|
+
}
|
|
25
|
+
export function learningSnapshotPath(sha256) {
|
|
26
|
+
if (!DIGEST.test(sha256))
|
|
27
|
+
throw new Error('invalid Learning snapshot SHA-256');
|
|
28
|
+
return `${LEARNING_SNAPSHOT_DIRECTORY}/${sha256}`;
|
|
29
|
+
}
|
|
30
|
+
/** Immutable objects live under the Home root, so Home archives carry them. */
|
|
31
|
+
class HomeSnapshotStore {
|
|
32
|
+
homeRoot;
|
|
33
|
+
directoryName;
|
|
34
|
+
constructor(homeRoot, directoryName) {
|
|
35
|
+
this.homeRoot = homeRoot;
|
|
36
|
+
this.directoryName = directoryName;
|
|
37
|
+
}
|
|
38
|
+
async retain(bytes) {
|
|
39
|
+
if (bytes.length > MAX_WORKLOG_FILE_BYTES)
|
|
40
|
+
throw new Error('file snapshot exceeds 8 MiB');
|
|
41
|
+
const sha256 = createHash('sha256').update(bytes).digest('hex');
|
|
42
|
+
const root = resolve(this.homeRoot);
|
|
43
|
+
if (await realpath(root) !== root)
|
|
44
|
+
throw new Error('snapshot Home root must be a real directory');
|
|
45
|
+
const directory = join(root, this.directoryName);
|
|
46
|
+
await mkdir(directory, { recursive: true });
|
|
47
|
+
if (await realpath(directory) !== directory)
|
|
48
|
+
throw new Error('snapshot directory traverses a symlink');
|
|
49
|
+
const target = join(directory, sha256);
|
|
50
|
+
const temporary = join(directory, `.${sha256}.${randomUUID()}.tmp`);
|
|
51
|
+
try {
|
|
52
|
+
await writeFile(temporary, bytes, { flag: 'wx', mode: 0o600 });
|
|
53
|
+
try {
|
|
54
|
+
await link(temporary, target);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (error.code !== 'EEXIST')
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
await rm(temporary, { force: true });
|
|
63
|
+
}
|
|
64
|
+
await this.read(sha256, bytes.length);
|
|
65
|
+
return { sha256, bytes: bytes.length };
|
|
66
|
+
}
|
|
67
|
+
async read(sha256, expectedBytes) {
|
|
68
|
+
const root = resolve(this.homeRoot);
|
|
69
|
+
const directory = join(root, this.directoryName);
|
|
70
|
+
if (await realpath(root) !== root || await realpath(directory) !== directory) {
|
|
71
|
+
throw new Error('snapshot directory traverses a symlink');
|
|
72
|
+
}
|
|
73
|
+
if (!DIGEST.test(sha256))
|
|
74
|
+
throw new Error('invalid snapshot SHA-256');
|
|
75
|
+
const target = join(directory, sha256);
|
|
76
|
+
const info = await lstat(target);
|
|
77
|
+
if (!info.isFile() || info.isSymbolicLink() || info.size > MAX_WORKLOG_FILE_BYTES
|
|
78
|
+
|| (expectedBytes !== undefined && info.size !== expectedBytes)) {
|
|
79
|
+
throw new Error('snapshot is missing, oversized, or changed');
|
|
80
|
+
}
|
|
81
|
+
const bytes = await readFile(target);
|
|
82
|
+
if (bytes.length !== info.size || createHash('sha256').update(bytes).digest('hex') !== sha256) {
|
|
83
|
+
throw new Error('snapshot checksum mismatch');
|
|
84
|
+
}
|
|
85
|
+
return bytes;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export class WorklogSnapshotStore extends HomeSnapshotStore {
|
|
89
|
+
constructor(homeRoot) { super(homeRoot, WORKLOG_SNAPSHOT_DIRECTORY); }
|
|
90
|
+
}
|
|
91
|
+
export class ProjectSnapshotStore extends HomeSnapshotStore {
|
|
92
|
+
constructor(homeRoot) { super(homeRoot, PROJECT_SNAPSHOT_DIRECTORY); }
|
|
93
|
+
}
|
|
94
|
+
export class WorkSnapshotStore extends HomeSnapshotStore {
|
|
95
|
+
constructor(homeRoot) { super(homeRoot, WORK_SNAPSHOT_DIRECTORY); }
|
|
96
|
+
}
|
|
97
|
+
export class LearningSnapshotStore extends HomeSnapshotStore {
|
|
98
|
+
constructor(homeRoot) { super(homeRoot, LEARNING_SNAPSHOT_DIRECTORY); }
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=worklog-snapshot.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const RUNTIME_WRITER_LOCK_NAME = ".bizagent-writer.lock";
|
|
2
|
+
export declare const RUNTIME_WRITER_CLAIM_NAME = ".bizagent-writer-claim.lock";
|
|
3
|
+
/** Prevent two DSH processes from using independent cached Domain snapshots for the same profile or Home root. */
|
|
4
|
+
export declare class RuntimeWriterGuard {
|
|
5
|
+
private readonly locks;
|
|
6
|
+
private released;
|
|
7
|
+
private constructor();
|
|
8
|
+
static acquire(roots: string[]): Promise<RuntimeWriterGuard>;
|
|
9
|
+
release(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=writer-guard.d.ts.map
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { open, mkdir, readFile, realpath, lstat, unlink } from 'node:fs/promises';
|
|
3
|
+
import { hostname } from 'node:os';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
export const RUNTIME_WRITER_LOCK_NAME = '.bizagent-writer.lock';
|
|
6
|
+
export const RUNTIME_WRITER_CLAIM_NAME = '.bizagent-writer-claim.lock';
|
|
7
|
+
const CLAIM_WAIT_MS = 2_000;
|
|
8
|
+
const CLAIM_POLL_MS = 25;
|
|
9
|
+
/** Lock and claim paths this process currently holds, so its own PID is not mistaken for a live owner. */
|
|
10
|
+
const heldInProcess = new Set();
|
|
11
|
+
/** Prevent two DSH processes from using independent cached Domain snapshots for the same profile or Home root. */
|
|
12
|
+
export class RuntimeWriterGuard {
|
|
13
|
+
locks;
|
|
14
|
+
released = false;
|
|
15
|
+
constructor(locks) {
|
|
16
|
+
this.locks = locks;
|
|
17
|
+
}
|
|
18
|
+
static async acquire(roots) {
|
|
19
|
+
const canonical = new Set();
|
|
20
|
+
for (const root of roots) {
|
|
21
|
+
await mkdir(root, { recursive: true });
|
|
22
|
+
canonical.add(await realpath(resolve(root)));
|
|
23
|
+
}
|
|
24
|
+
const held = [];
|
|
25
|
+
try {
|
|
26
|
+
for (const root of [...canonical].sort()) {
|
|
27
|
+
const path = join(root, RUNTIME_WRITER_LOCK_NAME);
|
|
28
|
+
const claim = await acquireClaim(join(root, RUNTIME_WRITER_CLAIM_NAME));
|
|
29
|
+
try {
|
|
30
|
+
const existing = await inspectLock(path);
|
|
31
|
+
if (existing) {
|
|
32
|
+
if (ownerMayBeLive(path, existing.owner)) {
|
|
33
|
+
throw new Error(`BizAgent writer is already reserved at ${path} `
|
|
34
|
+
+ `by ${existing.owner.hostname} PID ${existing.owner.pid}`);
|
|
35
|
+
}
|
|
36
|
+
const current = await lstat(path);
|
|
37
|
+
if (current.dev !== existing.device || current.ino !== existing.inode) {
|
|
38
|
+
throw new Error(`BizAgent writer lock changed during recovery: ${path}`);
|
|
39
|
+
}
|
|
40
|
+
await unlink(path);
|
|
41
|
+
}
|
|
42
|
+
held.push(await createHeld(path));
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
await releaseLocks([claim]);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return new RuntimeWriterGuard(held);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
await releaseLocks(held);
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async release() {
|
|
56
|
+
if (this.released)
|
|
57
|
+
return;
|
|
58
|
+
this.released = true;
|
|
59
|
+
await releaseLocks(this.locks);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function acquireClaim(path) {
|
|
63
|
+
const deadline = Date.now() + CLAIM_WAIT_MS;
|
|
64
|
+
while (true) {
|
|
65
|
+
try {
|
|
66
|
+
return await createHeld(path);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (!isExists(error))
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
let claim;
|
|
73
|
+
try {
|
|
74
|
+
claim = await inspectLock(path);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (Date.now() >= deadline)
|
|
78
|
+
throw error;
|
|
79
|
+
await new Promise(resolveDelay => setTimeout(resolveDelay, CLAIM_POLL_MS));
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (!claim) {
|
|
83
|
+
await new Promise(resolveDelay => setTimeout(resolveDelay, CLAIM_POLL_MS));
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (claim.owner.hostname !== hostname() || !ownerMayBeLive(path, claim.owner)) {
|
|
87
|
+
throw new Error(`BizAgent writer recovery claim is stale at ${path}; `
|
|
88
|
+
+ 'verify the prior process before removing it');
|
|
89
|
+
}
|
|
90
|
+
if (Date.now() >= deadline)
|
|
91
|
+
throw new Error(`BizAgent writer acquisition is busy at ${path}`);
|
|
92
|
+
await new Promise(resolveDelay => setTimeout(resolveDelay, CLAIM_POLL_MS));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function inspectLock(path) {
|
|
96
|
+
const info = await lstat(path).catch(error => {
|
|
97
|
+
if (isMissing(error))
|
|
98
|
+
return undefined;
|
|
99
|
+
throw error;
|
|
100
|
+
});
|
|
101
|
+
if (!info)
|
|
102
|
+
return undefined;
|
|
103
|
+
if (!info.isFile())
|
|
104
|
+
throw new Error(`BizAgent writer lock is not a regular file: ${path}`);
|
|
105
|
+
let raw;
|
|
106
|
+
try {
|
|
107
|
+
raw = await readFile(path, 'utf8');
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
if (isMissing(error))
|
|
111
|
+
return undefined;
|
|
112
|
+
throw new Error(`BizAgent writer lock is unreadable at ${path}; verify before removing it`);
|
|
113
|
+
}
|
|
114
|
+
let owner;
|
|
115
|
+
try {
|
|
116
|
+
owner = JSON.parse(raw);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
throw new Error(`BizAgent writer lock is unreadable at ${path}; verify before removing it`);
|
|
120
|
+
}
|
|
121
|
+
if (!validOwner(owner)) {
|
|
122
|
+
throw new Error(`BizAgent writer lock has invalid owner at ${path}; verify before removing it`);
|
|
123
|
+
}
|
|
124
|
+
return { device: info.dev, inode: info.ino, owner };
|
|
125
|
+
}
|
|
126
|
+
async function createHeld(path) {
|
|
127
|
+
const handle = await open(path, 'wx', 0o600);
|
|
128
|
+
try {
|
|
129
|
+
await handle.writeFile(JSON.stringify({ pid: process.pid, hostname: hostname(),
|
|
130
|
+
createdAt: new Date().toISOString(), token: randomUUID() }) + '\n');
|
|
131
|
+
await handle.sync();
|
|
132
|
+
const info = await handle.stat();
|
|
133
|
+
heldInProcess.add(path);
|
|
134
|
+
return { path, handle, device: info.dev, inode: info.ino };
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
const info = await handle.stat().catch(() => undefined);
|
|
138
|
+
await handle.close();
|
|
139
|
+
const current = await lstat(path).catch(() => undefined);
|
|
140
|
+
if (info && current && info.dev === current.dev && info.ino === current.ino) {
|
|
141
|
+
await unlink(path).catch(() => undefined);
|
|
142
|
+
}
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function validOwner(value) {
|
|
147
|
+
if (typeof value !== 'object' || value === null)
|
|
148
|
+
return false;
|
|
149
|
+
const owner = value;
|
|
150
|
+
return Number.isSafeInteger(owner['pid']) && Number(owner['pid']) > 0
|
|
151
|
+
&& typeof owner['hostname'] === 'string' && owner['hostname'].length > 0
|
|
152
|
+
&& typeof owner['token'] === 'string' && owner['token'].length > 0;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Whether a lock owner may still be running. Another host cannot be checked, so it counts as live.
|
|
156
|
+
* The current PID is live only while this process holds the path: after a crash, a restart that
|
|
157
|
+
* reuses the PID (for example PID 1 in a container) must not be blocked by its predecessor's lock.
|
|
158
|
+
*/
|
|
159
|
+
function ownerMayBeLive(path, owner) {
|
|
160
|
+
if (owner.hostname !== hostname())
|
|
161
|
+
return true;
|
|
162
|
+
if (owner.pid === process.pid)
|
|
163
|
+
return heldInProcess.has(path);
|
|
164
|
+
return processAlive(owner.pid);
|
|
165
|
+
}
|
|
166
|
+
function processAlive(pid) {
|
|
167
|
+
try {
|
|
168
|
+
process.kill(pid, 0);
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
return !(error instanceof Error && 'code' in error && error.code === 'ESRCH');
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
async function releaseLocks(locks) {
|
|
176
|
+
const errors = [];
|
|
177
|
+
for (const lock of [...locks].reverse()) {
|
|
178
|
+
try {
|
|
179
|
+
const current = await lstat(lock.path).catch(error => {
|
|
180
|
+
if (isMissing(error))
|
|
181
|
+
return undefined;
|
|
182
|
+
throw error;
|
|
183
|
+
});
|
|
184
|
+
if (current && (current.dev !== lock.device || current.ino !== lock.inode)) {
|
|
185
|
+
throw new Error(`BizAgent writer lock changed while held: ${lock.path}`);
|
|
186
|
+
}
|
|
187
|
+
if (current)
|
|
188
|
+
await unlink(lock.path);
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
errors.push(error);
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
heldInProcess.delete(lock.path);
|
|
195
|
+
await lock.handle.close().catch(error => { errors.push(error); });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (errors.length > 0)
|
|
199
|
+
throw new AggregateError(errors, 'BizAgent writer lock cleanup failed');
|
|
200
|
+
}
|
|
201
|
+
function isExists(error) {
|
|
202
|
+
return error instanceof Error && 'code' in error && error.code === 'EEXIST';
|
|
203
|
+
}
|
|
204
|
+
function isMissing(error) {
|
|
205
|
+
return error instanceof Error && 'code' in error && error.code === 'ENOENT';
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=writer-guard.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,174 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bizagent/dsh",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Long-term memory and teamwork for agents on DeepSeek Harness (DSH): Agent Homes, evidence-backed learning, governed sharing, Inbox, formal Work, and Projects.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/phonism/bizagent.git",
|
|
8
|
+
"directory": "packages/dsh"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/phonism/bizagent/tree/main/packages/dsh#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/phonism/bizagent/issues"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "./lib/index.js",
|
|
16
|
+
"types": "./lib/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./lib/index.d.ts",
|
|
20
|
+
"default": "./lib/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./host": {
|
|
23
|
+
"types": "./lib/host.d.ts",
|
|
24
|
+
"default": "./lib/host.js"
|
|
25
|
+
},
|
|
26
|
+
"./ui-host": {
|
|
27
|
+
"types": "./lib/ui-host.d.ts",
|
|
28
|
+
"default": "./lib/ui-host.js"
|
|
29
|
+
},
|
|
30
|
+
"./local-human-host": {
|
|
31
|
+
"types": "./lib/local-human-host.d.ts",
|
|
32
|
+
"default": "./lib/local-human-host.js"
|
|
33
|
+
},
|
|
34
|
+
"./client": {
|
|
35
|
+
"types": "./lib/client-types/client/index.d.ts",
|
|
36
|
+
"default": "./lib/client.js"
|
|
37
|
+
},
|
|
38
|
+
"./package.json": "./package.json"
|
|
39
|
+
},
|
|
40
|
+
"bin": {
|
|
41
|
+
"bizagent": "./lib/cli.js"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"lib/**/*.js",
|
|
45
|
+
"lib/**/*.d.ts",
|
|
46
|
+
"cordis.patch.yml",
|
|
47
|
+
"README.md",
|
|
48
|
+
"CHANGELOG.md",
|
|
49
|
+
"docs",
|
|
50
|
+
"examples",
|
|
51
|
+
"LICENSE"
|
|
52
|
+
],
|
|
53
|
+
"scripts": {
|
|
54
|
+
"clean": "node scripts/clean.mjs",
|
|
55
|
+
"build:host": "tsc -p tsconfig.json",
|
|
56
|
+
"build:client": "tsc -p tsconfig.client.json && node scripts/build-client.mjs",
|
|
57
|
+
"build": "npm run clean && npm run build:host && npm run build:client",
|
|
58
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.client.json --noEmit",
|
|
59
|
+
"test": "npm run build && node --test tests/*.test.mjs",
|
|
60
|
+
"examples": "npm run build && node examples/learning-lab.mjs",
|
|
61
|
+
"smoke": "npm run build && node scripts/smoke.mjs",
|
|
62
|
+
"smoke:profile": "npm run build && node scripts/profile-smoke.mjs",
|
|
63
|
+
"smoke:local-human": "npm run build && BIZAGENT_PROFILE_SMOKE_HUMAN=1 node scripts/profile-smoke.mjs",
|
|
64
|
+
"smoke:packed": "npm run build && node scripts/profile-smoke.mjs --packed",
|
|
65
|
+
"smoke:install": "npm run build && node scripts/installed-profile-smoke.mjs --install-only",
|
|
66
|
+
"smoke:installed": "npm run build && node scripts/installed-profile-smoke.mjs",
|
|
67
|
+
"smoke:plugin-add": "npm run build && node scripts/plugin-add-smoke.mjs",
|
|
68
|
+
"smoke:agent": "npm run build && node scripts/agent-profile-smoke.mjs",
|
|
69
|
+
"smoke:work": "npm run build && node scripts/work-profile-smoke.mjs",
|
|
70
|
+
"smoke:project": "npm run build && node scripts/project-profile-smoke.mjs",
|
|
71
|
+
"smoke:learning": "npm run build && node scripts/learning-profile-smoke.mjs",
|
|
72
|
+
"smoke:session-ui": "npm run build && node scripts/session-ui-smoke.mjs",
|
|
73
|
+
"pack:check": "npm pack --dry-run",
|
|
74
|
+
"docs:tools": "npm run build && node scripts/tools-reference.mjs"
|
|
75
|
+
},
|
|
76
|
+
"dsh": {
|
|
77
|
+
"bundle": {
|
|
78
|
+
"patch": "./cordis.patch.yml"
|
|
79
|
+
},
|
|
80
|
+
"client": {
|
|
81
|
+
"inject": [
|
|
82
|
+
"@deepseek-ai/dsh-api-session-controller",
|
|
83
|
+
"@deepseek-ai/dsh-api-workspace-controller",
|
|
84
|
+
"@deepseek-ai/dsh-client-locale",
|
|
85
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
86
|
+
"@deepseek-ai/dsh-client-ui-input-trigger",
|
|
87
|
+
"@deepseek-ai/dsh-client-ui-layout",
|
|
88
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
89
|
+
"@deepseek-ai/dsh-client-ui-session",
|
|
90
|
+
"@deepseek-ai/dsh-client-ui-sidebar",
|
|
91
|
+
"@deepseek-ai/dsh-client-ui-workspace",
|
|
92
|
+
"@deepseek-ai/dsh-client-ui-chat"
|
|
93
|
+
],
|
|
94
|
+
"platform": "web"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"engines": {
|
|
98
|
+
"node": "^22.19.0 || >=24.0.0"
|
|
99
|
+
},
|
|
100
|
+
"peerDependencies": {
|
|
101
|
+
"@deepseek-ai/cordis": "~4.0.5",
|
|
102
|
+
"@deepseek-ai/dsh-agent": "^0.2.1-rc.2",
|
|
103
|
+
"@deepseek-ai/dsh-agent-default-model": "^0.2.1-rc.2",
|
|
104
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.2.1-rc.2",
|
|
105
|
+
"@deepseek-ai/dsh-api-workspace-controller": "^0.2.1-rc.2",
|
|
106
|
+
"@deepseek-ai/dsh-attachment": "^0.2.1-rc.2",
|
|
107
|
+
"@deepseek-ai/dsh-client-locale": "^0.2.1-rc.2",
|
|
108
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.2.1-rc.2",
|
|
109
|
+
"@deepseek-ai/dsh-client-ui-input-trigger": "^0.2.1-rc.2",
|
|
110
|
+
"@deepseek-ai/dsh-client-ui-layout": "^0.2.1-rc.2",
|
|
111
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.2.1-rc.2",
|
|
112
|
+
"@deepseek-ai/dsh-client-ui-session": "^0.2.1-rc.2",
|
|
113
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "^0.2.1-rc.2",
|
|
114
|
+
"@deepseek-ai/dsh-client-ui-workspace": "^0.2.1-rc.2",
|
|
115
|
+
"@deepseek-ai/dsh-host-webserver": "^0.2.1-rc.2",
|
|
116
|
+
"@deepseek-ai/dsh-llm": "^0.2.1-rc.2",
|
|
117
|
+
"@deepseek-ai/dsh-session": "^0.2.1-rc.2",
|
|
118
|
+
"@deepseek-ai/dsh-session-query": "^0.2.1-rc.2",
|
|
119
|
+
"@deepseek-ai/dsh-skill": "^0.2.1-rc.2",
|
|
120
|
+
"@deepseek-ai/dsh-storage-domain": "^0.2.1-rc.2",
|
|
121
|
+
"@deepseek-ai/dsh-system-prompt": "^0.2.1-rc.2",
|
|
122
|
+
"@deepseek-ai/dsh-tools": "^0.2.1-rc.2",
|
|
123
|
+
"@deepseek-ai/dsh-client-ui-chat": "^0.2.1-rc.2"
|
|
124
|
+
},
|
|
125
|
+
"dependencies": {
|
|
126
|
+
"@deepseek-ai/dsh-util-values": "0.2.1-rc.2",
|
|
127
|
+
"@deepseek-ai/schemastery": "3.18.5",
|
|
128
|
+
"yaml": "2.9.0",
|
|
129
|
+
"zod": "4.4.3"
|
|
130
|
+
},
|
|
131
|
+
"devDependencies": {
|
|
132
|
+
"@deepseek-ai/cordis": "4.0.5",
|
|
133
|
+
"@deepseek-ai/dsh-agent": "0.2.1-rc.2",
|
|
134
|
+
"@deepseek-ai/dsh-agent-default-model": "0.2.1-rc.2",
|
|
135
|
+
"@deepseek-ai/dsh-api-remotes": "0.2.1-rc.2",
|
|
136
|
+
"@deepseek-ai/dsh-api-session-controller": "0.2.1-rc.2",
|
|
137
|
+
"@deepseek-ai/dsh-api-workspace-controller": "0.2.1-rc.2",
|
|
138
|
+
"@deepseek-ai/dsh-attachment": "0.2.1-rc.2",
|
|
139
|
+
"@deepseek-ai/dsh-client-locale": "0.2.1-rc.2",
|
|
140
|
+
"@deepseek-ai/dsh-client-store": "0.2.1-rc.2",
|
|
141
|
+
"@deepseek-ai/dsh-client-ui-chat": "0.2.1-rc.2",
|
|
142
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.2.1-rc.2",
|
|
143
|
+
"@deepseek-ai/dsh-client-ui-input-trigger": "0.2.1-rc.2",
|
|
144
|
+
"@deepseek-ai/dsh-client-ui-layout": "0.2.1-rc.2",
|
|
145
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.2.1-rc.2",
|
|
146
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.1-rc.2",
|
|
147
|
+
"@deepseek-ai/dsh-client-ui-session": "0.2.1-rc.2",
|
|
148
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "0.2.1-rc.2",
|
|
149
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.1-rc.2",
|
|
150
|
+
"@deepseek-ai/dsh-client-ui-workspace": "0.2.1-rc.2",
|
|
151
|
+
"@deepseek-ai/dsh-host-webserver": "0.2.1-rc.2",
|
|
152
|
+
"@deepseek-ai/dsh-llm": "0.2.1-rc.2",
|
|
153
|
+
"@deepseek-ai/dsh-session": "0.2.1-rc.2",
|
|
154
|
+
"@deepseek-ai/dsh-session-query": "0.2.1-rc.2",
|
|
155
|
+
"@deepseek-ai/dsh-skill": "0.2.1-rc.2",
|
|
156
|
+
"@deepseek-ai/dsh-storage-domain": "0.2.1-rc.2",
|
|
157
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.1-rc.2",
|
|
158
|
+
"@deepseek-ai/dsh-tools": "0.2.1-rc.2",
|
|
159
|
+
"@types/node": "^24.3.0",
|
|
160
|
+
"@types/react": "~18.3.1",
|
|
161
|
+
"@types/react-dom": "~18.3.1",
|
|
162
|
+
"esbuild": "^0.25.9",
|
|
163
|
+
"pnpm": "11.24.0",
|
|
164
|
+
"typescript": "^5.9.2"
|
|
165
|
+
},
|
|
166
|
+
"license": "MIT",
|
|
167
|
+
"keywords": [
|
|
168
|
+
"deepseek-harness",
|
|
169
|
+
"dsh",
|
|
170
|
+
"agent-memory",
|
|
171
|
+
"multi-agent",
|
|
172
|
+
"organizational-learning"
|
|
173
|
+
]
|
|
174
|
+
}
|