@wowyuarm/dsh-agent-team 0.1.12 → 0.1.13

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 (85) hide show
  1. package/README.md +2 -0
  2. package/README.zh.md +2 -0
  3. package/cordis.patch.yml +7 -3
  4. package/package.json +14 -2
  5. package/packages/agent-team/README.md +2 -2
  6. package/packages/agent-team/README.zh.md +2 -2
  7. package/packages/agent-team/lib/index.js +151 -69
  8. package/packages/agent-team/lib/ledger.js +249 -31
  9. package/packages/agent-team/lib/member-context.js +18 -1
  10. package/packages/agent-team/lib/member-runtime.js +5 -4
  11. package/packages/agent-team/lib/mentions.js +48 -24
  12. package/packages/agent-team/lib/spec.js +19 -0
  13. package/packages/agent-team/lib/typert.host.js +187 -41
  14. package/packages/agent-team/lib/typert.remote-client.d.ts +7 -3
  15. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  16. package/packages/agent-team/lib/typert.remote-client.js +132 -27
  17. package/packages/agent-team/lib/types/index.d.ts +32 -7
  18. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  19. package/packages/agent-team/lib/types/ledger.d.ts +46 -2
  20. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  21. package/packages/agent-team/lib/types/member-context.d.ts +6 -0
  22. package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
  23. package/packages/agent-team/lib/types/mentions.d.ts +20 -7
  24. package/packages/agent-team/lib/types/mentions.d.ts.map +1 -1
  25. package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
  26. package/packages/agent-team/lib/types/types/entities.d.ts +3 -2
  27. package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
  28. package/packages/agent-team/lib/types/types/operations.d.ts +37 -1
  29. package/packages/agent-team/lib/types/types/operations.d.ts.map +1 -1
  30. package/packages/agent-team/lib/types/types/requests-results.d.ts +50 -10
  31. package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
  32. package/packages/agent-team/lib/types/vendor/storage-sqlite/index.d.ts +87 -0
  33. package/packages/agent-team/lib/types/vendor/storage-sqlite/index.d.ts.map +1 -0
  34. package/packages/agent-team/lib/types/vendor/storage-sqlite/schema.d.ts +59 -0
  35. package/packages/agent-team/lib/types/vendor/storage-sqlite/schema.d.ts.map +1 -0
  36. package/packages/agent-team/lib/types/vendor/storage-sqlite/unit.d.ts +57 -0
  37. package/packages/agent-team/lib/types/vendor/storage-sqlite/unit.d.ts.map +1 -0
  38. package/packages/agent-team/lib/vendor/storage-sqlite/index.js +139 -0
  39. package/packages/agent-team/lib/vendor/storage-sqlite/schema.js +120 -0
  40. package/packages/agent-team/lib/vendor/storage-sqlite/unit.js +132 -0
  41. package/packages/client-agent-team/README.md +1 -1
  42. package/packages/client-agent-team/README.zh.md +1 -1
  43. package/packages/client-agent-team/lib/client.js +603 -343
  44. package/packages/client-agent-team/lib/client.js.map +1 -1
  45. package/packages/client-agent-team/lib/types/client/TeamAgentImport.d.ts +12 -0
  46. package/packages/client-agent-team/lib/types/client/TeamAgentImport.d.ts.map +1 -0
  47. package/packages/client-agent-team/lib/types/client/TeamAgentImport.js +69 -0
  48. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +3 -1
  49. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  50. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +29 -17
  51. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  52. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +19 -44
  53. package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts +2 -2
  54. package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts.map +1 -1
  55. package/packages/client-agent-team/lib/types/client/TeamInboxPage.js +15 -14
  56. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +1 -1
  57. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -1
  58. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  59. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +15 -29
  60. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
  61. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
  62. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +2 -2
  63. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  64. package/packages/client-agent-team/lib/types/client/index.js +8 -1
  65. package/packages/client-agent-team/lib/types/client/locales.d.ts +18 -0
  66. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  67. package/packages/client-agent-team/lib/types/client/locales.js +18 -0
  68. package/packages/client-agent-team/lib/types/client/slots.d.ts +3 -1
  69. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  70. package/packages/client-agent-team/lib/types/client/team-changes.d.ts +15 -31
  71. package/packages/client-agent-team/lib/types/client/team-changes.d.ts.map +1 -1
  72. package/packages/client-agent-team/lib/types/client/team-changes.js +82 -111
  73. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +13 -11
  74. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
  75. package/packages/client-agent-team/lib/types/client/team-formatters.js +29 -35
  76. package/packages/tool-agent-team/README.md +1 -1
  77. package/packages/tool-agent-team/README.zh.md +1 -1
  78. package/packages/tool-agent-team/lib/host-access.js +19 -0
  79. package/packages/tool-agent-team/lib/index.js +42 -33
  80. package/packages/tool-agent-team/lib/types/host-access.d.ts +10 -0
  81. package/packages/tool-agent-team/lib/types/host-access.d.ts.map +1 -1
  82. package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
  83. package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts +0 -41
  84. package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts.map +0 -1
  85. package/packages/client-agent-team/lib/types/client/scope-coverage.js +0 -64
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Vendored fork of `@deepseek-ai/dsh-storage-sqlite` (`index.ts`).
3
+ *
4
+ * Source: deepseek-ai/deepseek-harness
5
+ * `packages/storage/storage-sqlite/src/index.ts` at version 0.1.5-rc.2
6
+ * (MIT per that version's package metadata and the repo root LICENSE;
7
+ * the stale npm `latest` tag 0.0.1-rc.1 carries BSD-3-Clause metadata).
8
+ * Kept identical except the `@module` tag below.
9
+ *
10
+ * Why vendored instead of depended on: DSH Desktop generation installers
11
+ * strip `@deepseek-ai/*` copies and resolve them from the host closure,
12
+ * which ships no `dsh-storage-sqlite` — a loader row naming that package
13
+ * blocks boot (GitHub issue #28). Resolving from our own package survives
14
+ * that boundary. Sync procedure: diff against the upstream file on every
15
+ * DSH compat round (see docs/dsh-release-compatibility.md).
16
+ */
17
+ /**
18
+ * SQLite storage backend for the storage hub: one database file hosts every
19
+ * routed unit, document-per-row (`key TEXT` / `value TEXT` JSON). Registers
20
+ * as backend `sqlite`; the disposer unregisters first, then closes the medium.
21
+ * @module @wowyuarm/dsh-agent-team/sqlite-backend
22
+ */
23
+ import type { Context } from '@deepseek-ai/cordis';
24
+ import z from '@deepseek-ai/schemastery';
25
+ import type { KvFacet, StorageBackend } from '@deepseek-ai/dsh-storage';
26
+ import { type JournalMode } from './schema.ts';
27
+ export { STORAGE_SQLITE_SCHEMA_VERSION, type JournalMode } from './schema.ts';
28
+ /** Cordis plugin name. */
29
+ export declare const name = "storage-sqlite";
30
+ /** The backend registers on the storage hub. */
31
+ export declare const inject: string[];
32
+ /** Plugin configuration. */
33
+ export interface Config {
34
+ /**
35
+ * Filesystem path to the SQLite database file. The special value `:memory:`
36
+ * opens an in-process database (tests). On filesystems with POSIX modes,
37
+ * missing directories and databases are created owner-only; existing path
38
+ * modes are preserved. Filesystem setup errors other than an existing
39
+ * database fail the open. The backend does not protect confidentiality or
40
+ * integrity when another principal can replace the database entry in its
41
+ * parent directory.
42
+ */
43
+ path: string;
44
+ /**
45
+ * SQLite `journal_mode` pragma. `wal` (the default) suits local disks; pick
46
+ * a rollback-journal mode (`delete`/`truncate`/`persist`) on filesystems
47
+ * where WAL's shared-memory files do not work (network mounts). See
48
+ * {@link JournalMode}.
49
+ */
50
+ journalMode?: JournalMode;
51
+ }
52
+ /** Schemastery validator for {@link Config}. */
53
+ export declare const Config: z<Config>;
54
+ /**
55
+ * The SQLite {@link StorageBackend}. Owns one `DatabaseSync` connection and
56
+ * the open-unit table; `kv.open` validates names, enforces the per-unit
57
+ * version stamp in `units`, and ensures the unit's record tables.
58
+ */
59
+ export declare class SqliteStorageBackend implements StorageBackend {
60
+ /** The key-value facet; the only shape this backend serves. */
61
+ readonly kv: KvFacet;
62
+ private readonly ready;
63
+ /** Open (or still-opening) units by name; presence is the double-open guard. */
64
+ private readonly units;
65
+ private closing;
66
+ /**
67
+ * @param config - Validated plugin configuration.
68
+ */
69
+ constructor(config: Config);
70
+ private openUnit;
71
+ private materializeUnit;
72
+ /**
73
+ * Close every open unit and release the database. Idempotent; concurrent
74
+ * and repeated calls resolve once teardown finishes.
75
+ * @returns resolution after the medium is released.
76
+ */
77
+ close(): Promise<void>;
78
+ private doClose;
79
+ }
80
+ /**
81
+ * Register the SQLite backend as `sqlite` on the storage hub. The disposer
82
+ * unregisters the name first, then closes the backend.
83
+ * @param ctx - Plugin context (must inject `storage`).
84
+ * @param config - Validated plugin configuration.
85
+ */
86
+ export declare function apply(ctx: Context, config: Config): void;
87
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/vendor/storage-sqlite/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAGxC,OAAO,KAAK,EAAE,OAAO,EAA4B,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACjG,OAAO,EAAiC,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAG7E,OAAO,EAAE,6BAA6B,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAE7E,0BAA0B;AAC1B,eAAO,MAAM,IAAI,mBAAmB,CAAA;AACpC,gDAAgD;AAChD,eAAO,MAAM,MAAM,UAAc,CAAA;AAEjC,4BAA4B;AAC5B,MAAM,WAAW,MAAM;IACrB;;;;;;;;OAQG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;AAED,gDAAgD;AAChD,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAG3B,CAAA;AAEF;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IACzD,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAoD;IAExE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAC7C,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2C;IACjE,OAAO,CAAC,OAAO,CAA2B;IAE1C;;OAEG;gBACS,MAAM,EAAE,MAAM;IAQ1B,OAAO,CAAC,QAAQ;YAuBF,eAAe;IA2B7B;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAKR,OAAO;CAetB;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAUjD"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Vendored fork of `@deepseek-ai/dsh-storage-sqlite` (`schema.ts`).
3
+ *
4
+ * Source: deepseek-ai/deepseek-harness
5
+ * `packages/storage/storage-sqlite/src/schema.ts` at version 0.1.5-rc.2
6
+ * (MIT per that version's package metadata and the repo root LICENSE;
7
+ * the stale npm `latest` tag 0.0.1-rc.1 carries BSD-3-Clause metadata).
8
+ * Kept identical except the `@module` tag below.
9
+ *
10
+ * Why vendored instead of depended on: DSH Desktop generation installers
11
+ * strip `@deepseek-ai/*` copies and resolve them from the host closure,
12
+ * which ships no `dsh-storage-sqlite` — a loader row naming that package
13
+ * blocks boot (GitHub issue #28). Resolving from our own package survives
14
+ * that boundary. Sync procedure: diff against the upstream file on every
15
+ * DSH compat round (see docs/dsh-release-compatibility.md).
16
+ */
17
+ /**
18
+ * Schema + open-time helpers for the SQLite storage backend: the physical
19
+ * layout version, the database open/configure sequence (permissions, pragmas,
20
+ * version stamp/reject), and the unit metadata tables. Unit record tables are
21
+ * created per descriptor in `unit.ts`.
22
+ * @module @wowyuarm/dsh-agent-team/sqlite-backend/schema
23
+ */
24
+ import { DatabaseSync } from 'node:sqlite';
25
+ /**
26
+ * The on-disk physical layout version, stored in `PRAGMA user_version`.
27
+ * Orthogonal to each unit's own `version` (stamped per unit in the `units`
28
+ * row). Bumped only on a breaking change to the table layout; any other
29
+ * stamped version rejects — this unreleased format has no migrations.
30
+ */
31
+ export declare const STORAGE_SQLITE_SCHEMA_VERSION = 1;
32
+ /**
33
+ * Journal modes the backend will run under. `wal` is the default; the
34
+ * rollback-journal modes (`delete`/`truncate`/`persist`) exist for
35
+ * filesystems where WAL's shared-memory files do not work (network mounts).
36
+ * `memory`/`off` are excluded: dropping journal durability silently
37
+ * contradicts the durability clause of the KV backend contract.
38
+ */
39
+ export type JournalMode = 'wal' | 'delete' | 'truncate' | 'persist';
40
+ /**
41
+ * Open the database and apply its schema and pragmas. Missing directories and
42
+ * database files are created owner-only (`:memory:` skips filesystem setup).
43
+ * A zero `user_version` is stamped with {@link STORAGE_SQLITE_SCHEMA_VERSION};
44
+ * every other non-current version rejects rather than being migrated in place.
45
+ * @param path - the SQLite database file to open, or `:memory:`.
46
+ * @param journalMode - validated journal pragma.
47
+ * @returns the open handle with pragmas applied and the unit metadata tables ensured.
48
+ */
49
+ export declare function openDatabase(path: string, journalMode: JournalMode): Promise<DatabaseSync>;
50
+ /**
51
+ * Physical table name for one unit table. Both segments are validated against
52
+ * `UNIT_NAME_RE` before reaching this, so the result is safe to interpolate
53
+ * into DDL and prepared-statement text.
54
+ * @param unit - Validated unit name.
55
+ * @param table - Validated table name.
56
+ * @returns the `u_<unit>_<table>` identifier.
57
+ */
58
+ export declare function recordTableName(unit: string, table: string): string;
59
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/vendor/storage-sqlite/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAK1C;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,IAAI,CAAA;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAA;AAsBnE;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAchG;AAmCD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnE"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Vendored fork of `@deepseek-ai/dsh-storage-sqlite` (`unit.ts`).
3
+ *
4
+ * Source: deepseek-ai/deepseek-harness
5
+ * `packages/storage/storage-sqlite/src/unit.ts` at version 0.1.5-rc.2
6
+ * (MIT per that version's package metadata and the repo root LICENSE;
7
+ * the stale npm `latest` tag 0.0.1-rc.1 carries BSD-3-Clause metadata).
8
+ * Kept identical except the `@module` tag below — see `index.ts` for why
9
+ * this backend is vendored and how to sync it.
10
+ */
11
+ /**
12
+ * One opened SQLite KV unit: prepared per-table statements over the
13
+ * `u_<unit>_<table>` record tables plus this unit's row in the shared
14
+ * `unit_globals` table. Each primitive is a single statement, so atomicity
15
+ * comes from SQLite itself — no explicit transactions, and no write queue
16
+ * (write ordering is the caller's responsibility per the KV contract).
17
+ * @module @wowyuarm/dsh-agent-team/sqlite-backend/unit
18
+ */
19
+ import type { DatabaseSync } from 'node:sqlite';
20
+ import type { KvUnit, KvUnitDescriptor } from '@deepseek-ai/dsh-storage';
21
+ /**
22
+ * The SQLite {@link KvUnit}. Constructed by the backend AFTER the unit's
23
+ * record tables exist; statements are prepared once here and reused for every
24
+ * primitive. Values are stored as JSON text in the `value` column.
25
+ */
26
+ export declare class SqliteKvUnit implements KvUnit {
27
+ private readonly descriptor;
28
+ private readonly onClose;
29
+ private readonly tables;
30
+ private readonly globalUpsert;
31
+ private readonly globalSelect;
32
+ private closed;
33
+ /**
34
+ * @param db - Open database handle owned by the backend (never closed here).
35
+ * @param descriptor - Validated descriptor whose record tables already exist.
36
+ * @param onClose - Backend callback releasing this unit's open-name slot.
37
+ */
38
+ constructor(db: DatabaseSync, descriptor: KvUnitDescriptor, onClose: () => void);
39
+ loadAll(): Promise<{
40
+ tables: Record<string, Record<string, unknown>>;
41
+ global: unknown;
42
+ }>;
43
+ /** Parse one stored value column, mapping bad JSON to `malformed-medium`. */
44
+ private parseValue;
45
+ putRecord(table: string, key: string, value: unknown): Promise<void>;
46
+ deleteRecord(table: string, key: string): Promise<void>;
47
+ setGlobal(value: unknown): Promise<void>;
48
+ close(): Promise<void>;
49
+ /**
50
+ * Run one synchronous primitive behind the closed guard, mapping a throw to
51
+ * a rejection so the Promise-returning contract never throws synchronously.
52
+ */
53
+ private settle;
54
+ private ensureOpen;
55
+ private statementsFor;
56
+ }
57
+ //# sourceMappingURL=unit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unit.d.ts","sourceRoot":"","sources":["../../../../src/vendor/storage-sqlite/unit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAA;AAE9D,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAUxE;;;;GAIG;AACH,qBAAa,YAAa,YAAW,MAAM;IAavC,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAb1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqC;IAC5D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA2B;IACxD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA2B;IACxD,OAAO,CAAC,MAAM,CAAQ;IAEtB;;;;OAIG;gBAED,EAAE,EAAE,YAAY,EACC,UAAU,EAAE,gBAAgB,EAC5B,OAAO,EAAE,MAAM,IAAI;IAwBtC,OAAO,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;IAqBxF,6EAA6E;IAC7E,OAAO,CAAC,UAAU;IAYlB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpE,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMvD,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IASxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB;;;OAGG;IACH,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,aAAa;CAOtB"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Vendored fork of `@deepseek-ai/dsh-storage-sqlite` (`index.ts`).
3
+ *
4
+ * Source: deepseek-ai/deepseek-harness
5
+ * `packages/storage/storage-sqlite/src/index.ts` at version 0.1.5-rc.2
6
+ * (MIT per that version's package metadata and the repo root LICENSE;
7
+ * the stale npm `latest` tag 0.0.1-rc.1 carries BSD-3-Clause metadata).
8
+ * Kept identical except the `@module` tag below.
9
+ *
10
+ * Why vendored instead of depended on: DSH Desktop generation installers
11
+ * strip `@deepseek-ai/*` copies and resolve them from the host closure,
12
+ * which ships no `dsh-storage-sqlite` — a loader row naming that package
13
+ * blocks boot (GitHub issue #28). Resolving from our own package survives
14
+ * that boundary. Sync procedure: diff against the upstream file on every
15
+ * DSH compat round (see docs/dsh-release-compatibility.md).
16
+ */
17
+ import z from '@deepseek-ai/schemastery';
18
+ import { StorageError, UNIT_NAME_RE, storageBackendServiceKey } from '@deepseek-ai/dsh-storage';
19
+ import { openDatabase, recordTableName } from "./schema.js";
20
+ import { SqliteKvUnit } from "./unit.js";
21
+ export { STORAGE_SQLITE_SCHEMA_VERSION } from "./schema.js";
22
+ /** Cordis plugin name. */
23
+ export const name = 'storage-sqlite';
24
+ /** The backend registers on the storage hub. */
25
+ export const inject = ['storage'];
26
+ /** Schemastery validator for {@link Config}. */
27
+ export const Config = z.object({
28
+ path: z.string().required(),
29
+ journalMode: z.union(['wal', 'delete', 'truncate', 'persist']).default('wal'),
30
+ });
31
+ /**
32
+ * The SQLite {@link StorageBackend}. Owns one `DatabaseSync` connection and
33
+ * the open-unit table; `kv.open` validates names, enforces the per-unit
34
+ * version stamp in `units`, and ensures the unit's record tables.
35
+ */
36
+ export class SqliteStorageBackend {
37
+ /** The key-value facet; the only shape this backend serves. */
38
+ kv = { open: descriptor => this.openUnit(descriptor) };
39
+ ready;
40
+ /** Open (or still-opening) units by name; presence is the double-open guard. */
41
+ units = new Map();
42
+ closing;
43
+ /**
44
+ * @param config - Validated plugin configuration.
45
+ */
46
+ constructor(config) {
47
+ this.ready = openDatabase(config.path, config.journalMode);
48
+ // Mark the rejection handled: every primitive re-awaits `ready`, so an
49
+ // open failure still surfaces to each caller; this guard only prevents an
50
+ // unhandled-rejection crash when the failure precedes the first use.
51
+ this.ready.catch(() => { });
52
+ }
53
+ openUnit(descriptor) {
54
+ if (this.closing !== undefined) {
55
+ return Promise.reject(new StorageError('closed', 'sqlite storage backend is closed'));
56
+ }
57
+ if (!UNIT_NAME_RE.test(descriptor.name)) {
58
+ return Promise.reject(new Error(`kv unit name '${descriptor.name}' violates ${UNIT_NAME_RE}`));
59
+ }
60
+ for (const table of descriptor.tables) {
61
+ if (!UNIT_NAME_RE.test(table)) {
62
+ return Promise.reject(new Error(`kv table name '${table}' in unit '${descriptor.name}' violates ${UNIT_NAME_RE}`));
63
+ }
64
+ }
65
+ if (this.units.has(descriptor.name)) {
66
+ return Promise.reject(new Error(`kv unit '${descriptor.name}' is already open (double-open is a caller bug)`));
67
+ }
68
+ // Reserve the name synchronously so a concurrent second open of the same
69
+ // name rejects instead of racing past the guard during the awaits below.
70
+ const pending = this.materializeUnit(descriptor);
71
+ this.units.set(descriptor.name, pending);
72
+ pending.catch(() => this.units.delete(descriptor.name));
73
+ return pending;
74
+ }
75
+ async materializeUnit(descriptor) {
76
+ const db = await this.ready;
77
+ const row = db.prepare('SELECT version FROM units WHERE name = ?').get(descriptor.name);
78
+ if (row === undefined) {
79
+ db.prepare('INSERT INTO units (name, version) VALUES (?, ?)').run(descriptor.name, descriptor.version);
80
+ }
81
+ else if (row.version !== descriptor.version) {
82
+ throw new StorageError('version-mismatch', `kv unit '${descriptor.name}' is stamped version ${row.version} on the medium, incompatible with descriptor version ${descriptor.version}`);
83
+ }
84
+ for (const table of descriptor.tables) {
85
+ // Both segments passed UNIT_NAME_RE, so the identifier is safe in DDL.
86
+ db.exec(`
87
+ CREATE TABLE IF NOT EXISTS "${recordTableName(descriptor.name, table)}" (
88
+ key TEXT PRIMARY KEY,
89
+ value TEXT NOT NULL
90
+ ) STRICT
91
+ `);
92
+ }
93
+ return new SqliteKvUnit(db, descriptor, () => {
94
+ this.units.delete(descriptor.name);
95
+ });
96
+ }
97
+ /**
98
+ * Close every open unit and release the database. Idempotent; concurrent
99
+ * and repeated calls resolve once teardown finishes.
100
+ * @returns resolution after the medium is released.
101
+ */
102
+ close() {
103
+ this.closing ??= this.doClose();
104
+ return this.closing;
105
+ }
106
+ async doClose() {
107
+ let db;
108
+ try {
109
+ db = await this.ready;
110
+ }
111
+ catch {
112
+ // The medium never opened; that failure already rejected the opener and
113
+ // every unit call, so there is nothing left to release here.
114
+ return;
115
+ }
116
+ for (const pending of [...this.units.values()]) {
117
+ const unit = await pending.catch(() => undefined);
118
+ await unit?.close();
119
+ }
120
+ db.close();
121
+ }
122
+ }
123
+ /**
124
+ * Register the SQLite backend as `sqlite` on the storage hub. The disposer
125
+ * unregisters the name first, then closes the backend.
126
+ * @param ctx - Plugin context (must inject `storage`).
127
+ * @param config - Validated plugin configuration.
128
+ */
129
+ export function apply(ctx, config) {
130
+ const backend = new SqliteStorageBackend(config);
131
+ ctx.effect(() => {
132
+ const dispose = ctx.storage.backend.register('sqlite', backend);
133
+ return async () => {
134
+ dispose();
135
+ await backend.close();
136
+ };
137
+ }, 'storage-sqlite.registerBackend');
138
+ ctx.provide(storageBackendServiceKey('sqlite'), backend);
139
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Vendored fork of `@deepseek-ai/dsh-storage-sqlite` (`schema.ts`).
3
+ *
4
+ * Source: deepseek-ai/deepseek-harness
5
+ * `packages/storage/storage-sqlite/src/schema.ts` at version 0.1.5-rc.2
6
+ * (MIT per that version's package metadata and the repo root LICENSE;
7
+ * the stale npm `latest` tag 0.0.1-rc.1 carries BSD-3-Clause metadata).
8
+ * Kept identical except the `@module` tag below.
9
+ *
10
+ * Why vendored instead of depended on: DSH Desktop generation installers
11
+ * strip `@deepseek-ai/*` copies and resolve them from the host closure,
12
+ * which ships no `dsh-storage-sqlite` — a loader row naming that package
13
+ * blocks boot (GitHub issue #28). Resolving from our own package survives
14
+ * that boundary. Sync procedure: diff against the upstream file on every
15
+ * DSH compat round (see docs/dsh-release-compatibility.md).
16
+ */
17
+ /**
18
+ * Schema + open-time helpers for the SQLite storage backend: the physical
19
+ * layout version, the database open/configure sequence (permissions, pragmas,
20
+ * version stamp/reject), and the unit metadata tables. Unit record tables are
21
+ * created per descriptor in `unit.ts`.
22
+ * @module @wowyuarm/dsh-agent-team/sqlite-backend/schema
23
+ */
24
+ import { DatabaseSync } from 'node:sqlite';
25
+ import { mkdir, open } from 'node:fs/promises';
26
+ import { dirname, resolve } from 'node:path';
27
+ import { StorageError } from '@deepseek-ai/dsh-storage';
28
+ /**
29
+ * The on-disk physical layout version, stored in `PRAGMA user_version`.
30
+ * Orthogonal to each unit's own `version` (stamped per unit in the `units`
31
+ * row). Bumped only on a breaking change to the table layout; any other
32
+ * stamped version rejects — this unreleased format has no migrations.
33
+ */
34
+ export const STORAGE_SQLITE_SCHEMA_VERSION = 1;
35
+ /* jscpd:ignore-start -- deliberately mirrors the session-query-sqlite open
36
+ sequence. Each package owns a distinct database identity and schema, so a
37
+ shared helper would couple otherwise independent storage providers (see the
38
+ domain KV storage Agent Note's reuse audit). */
39
+ /**
40
+ * Exclusively create a missing database file with owner-only permissions.
41
+ * Existing files retain their modes, and errors other than `EEXIST` propagate.
42
+ * `DatabaseSync` reopens by path, so this does not protect confidentiality or
43
+ * integrity when another principal can replace the database entry in its
44
+ * parent directory.
45
+ */
46
+ async function createDatabaseFile(path) {
47
+ try {
48
+ const handle = await open(path, 'wx', 0o600);
49
+ await handle.close();
50
+ }
51
+ catch (error) {
52
+ if (error.code !== 'EEXIST')
53
+ throw error;
54
+ }
55
+ }
56
+ /**
57
+ * Open the database and apply its schema and pragmas. Missing directories and
58
+ * database files are created owner-only (`:memory:` skips filesystem setup).
59
+ * A zero `user_version` is stamped with {@link STORAGE_SQLITE_SCHEMA_VERSION};
60
+ * every other non-current version rejects rather than being migrated in place.
61
+ * @param path - the SQLite database file to open, or `:memory:`.
62
+ * @param journalMode - validated journal pragma.
63
+ * @returns the open handle with pragmas applied and the unit metadata tables ensured.
64
+ */
65
+ export async function openDatabase(path, journalMode) {
66
+ const actual = path === ':memory:' ? path : resolve(path);
67
+ if (actual !== ':memory:') {
68
+ await mkdir(dirname(actual), { recursive: true, mode: 0o700 });
69
+ await createDatabaseFile(actual);
70
+ }
71
+ const db = new DatabaseSync(actual);
72
+ try {
73
+ configureDatabase(db, actual, journalMode);
74
+ return db;
75
+ }
76
+ catch (error) {
77
+ db.close();
78
+ throw error;
79
+ }
80
+ }
81
+ function configureDatabase(db, path, journalMode) {
82
+ db.exec('PRAGMA foreign_keys = ON');
83
+ // The validated union is safe to interpolate into a non-bindable PRAGMA.
84
+ db.exec(`PRAGMA journal_mode = ${journalMode.toUpperCase()}`);
85
+ // `PRAGMA user_version` always returns exactly one row { user_version }.
86
+ const { user_version: onDisk } = db.prepare('PRAGMA user_version').get();
87
+ if (onDisk !== 0 && onDisk !== STORAGE_SQLITE_SCHEMA_VERSION) {
88
+ throw new StorageError('version-mismatch', `storage database at "${path}" has schema version ${onDisk}, incompatible with this build (${STORAGE_SQLITE_SCHEMA_VERSION})`);
89
+ }
90
+ /* jscpd:ignore-end */
91
+ db.exec(`
92
+ CREATE TABLE IF NOT EXISTS units (
93
+ name TEXT PRIMARY KEY,
94
+ version INTEGER NOT NULL
95
+ ) STRICT
96
+ `);
97
+ db.exec(`
98
+ CREATE TABLE IF NOT EXISTS unit_globals (
99
+ unit TEXT PRIMARY KEY REFERENCES units(name),
100
+ value TEXT NOT NULL
101
+ ) STRICT
102
+ `);
103
+ if (onDisk === 0) {
104
+ // Stamp fresh databases LAST: the stamp asserts the layout is complete,
105
+ // so a failure above must leave the medium unstamped (a re-open after
106
+ // the obstruction is cleared retries materialization from scratch).
107
+ db.exec(`PRAGMA user_version = ${STORAGE_SQLITE_SCHEMA_VERSION}`);
108
+ }
109
+ }
110
+ /**
111
+ * Physical table name for one unit table. Both segments are validated against
112
+ * `UNIT_NAME_RE` before reaching this, so the result is safe to interpolate
113
+ * into DDL and prepared-statement text.
114
+ * @param unit - Validated unit name.
115
+ * @param table - Validated table name.
116
+ * @returns the `u_<unit>_<table>` identifier.
117
+ */
118
+ export function recordTableName(unit, table) {
119
+ return `u_${unit}_${table}`;
120
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Vendored fork of `@deepseek-ai/dsh-storage-sqlite` (`unit.ts`).
3
+ *
4
+ * Source: deepseek-ai/deepseek-harness
5
+ * `packages/storage/storage-sqlite/src/unit.ts` at version 0.1.5-rc.2
6
+ * (MIT per that version's package metadata and the repo root LICENSE;
7
+ * the stale npm `latest` tag 0.0.1-rc.1 carries BSD-3-Clause metadata).
8
+ * Kept identical except the `@module` tag below — see `index.ts` for why
9
+ * this backend is vendored and how to sync it.
10
+ */
11
+ import { StorageError } from '@deepseek-ai/dsh-storage';
12
+ import { recordTableName } from "./schema.js";
13
+ /**
14
+ * The SQLite {@link KvUnit}. Constructed by the backend AFTER the unit's
15
+ * record tables exist; statements are prepared once here and reused for every
16
+ * primitive. Values are stored as JSON text in the `value` column.
17
+ */
18
+ export class SqliteKvUnit {
19
+ descriptor;
20
+ onClose;
21
+ tables = new Map();
22
+ globalUpsert;
23
+ globalSelect;
24
+ closed = false;
25
+ /**
26
+ * @param db - Open database handle owned by the backend (never closed here).
27
+ * @param descriptor - Validated descriptor whose record tables already exist.
28
+ * @param onClose - Backend callback releasing this unit's open-name slot.
29
+ */
30
+ constructor(db, descriptor, onClose) {
31
+ this.descriptor = descriptor;
32
+ this.onClose = onClose;
33
+ for (const table of descriptor.tables) {
34
+ // Both name segments are validated against UNIT_NAME_RE by the backend,
35
+ // so the physical identifier is safe to interpolate into statement text.
36
+ const physical = recordTableName(descriptor.name, table);
37
+ this.tables.set(table, {
38
+ upsert: db.prepare(`INSERT INTO "${physical}" (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value`),
39
+ remove: db.prepare(`DELETE FROM "${physical}" WHERE key = ?`),
40
+ selectAll: db.prepare(`SELECT key, value FROM "${physical}"`),
41
+ });
42
+ }
43
+ this.globalUpsert = descriptor.hasGlobal
44
+ ? db.prepare('INSERT INTO unit_globals (unit, value) VALUES (?, ?) ON CONFLICT(unit) DO UPDATE SET value = excluded.value')
45
+ : undefined;
46
+ this.globalSelect = descriptor.hasGlobal
47
+ ? db.prepare('SELECT value FROM unit_globals WHERE unit = ?')
48
+ : undefined;
49
+ }
50
+ loadAll() {
51
+ return this.settle(() => {
52
+ const tables = {};
53
+ for (const [name, statements] of this.tables) {
54
+ // Null prototype: record keys are arbitrary strings, so '__proto__'
55
+ // must land as an own property instead of mutating the prototype.
56
+ const records = Object.create(null);
57
+ for (const row of statements.selectAll.all()) {
58
+ records[row.key] = this.parseValue(row.value, `table '${name}' key '${row.key}'`);
59
+ }
60
+ tables[name] = records;
61
+ }
62
+ let global = null;
63
+ if (this.globalSelect !== undefined) {
64
+ const row = this.globalSelect.get(this.descriptor.name);
65
+ if (row !== undefined)
66
+ global = this.parseValue(row.value, 'global slot');
67
+ }
68
+ return { tables, global };
69
+ });
70
+ }
71
+ /** Parse one stored value column, mapping bad JSON to `malformed-medium`. */
72
+ parseValue(text, slot) {
73
+ try {
74
+ return JSON.parse(text);
75
+ }
76
+ catch (error) {
77
+ throw new StorageError('malformed-medium', `kv unit '${this.descriptor.name}' holds unparsable JSON at ${slot}`, { cause: error });
78
+ }
79
+ }
80
+ putRecord(table, key, value) {
81
+ return this.settle(() => {
82
+ this.statementsFor(table).upsert.run(key, JSON.stringify(value));
83
+ });
84
+ }
85
+ deleteRecord(table, key) {
86
+ return this.settle(() => {
87
+ this.statementsFor(table).remove.run(key);
88
+ });
89
+ }
90
+ setGlobal(value) {
91
+ return this.settle(() => {
92
+ if (this.globalUpsert === undefined) {
93
+ throw new Error(`kv unit '${this.descriptor.name}' declared no global slot`);
94
+ }
95
+ this.globalUpsert.run(this.descriptor.name, JSON.stringify(value));
96
+ });
97
+ }
98
+ close() {
99
+ if (!this.closed) {
100
+ this.closed = true;
101
+ this.onClose();
102
+ }
103
+ return Promise.resolve();
104
+ }
105
+ /**
106
+ * Run one synchronous primitive behind the closed guard, mapping a throw to
107
+ * a rejection so the Promise-returning contract never throws synchronously.
108
+ */
109
+ settle(operation) {
110
+ try {
111
+ this.ensureOpen();
112
+ return Promise.resolve(operation());
113
+ }
114
+ catch (error) {
115
+ // Non-Error throws can only enter through JSON.stringify propagating a
116
+ // value's own toJSON throw; wrap those, preserve every real Error.
117
+ return Promise.reject(error instanceof Error ? error : new Error(String(error)));
118
+ }
119
+ }
120
+ ensureOpen() {
121
+ if (this.closed) {
122
+ throw new StorageError('closed', `kv unit '${this.descriptor.name}' is closed`);
123
+ }
124
+ }
125
+ statementsFor(table) {
126
+ const statements = this.tables.get(table);
127
+ if (statements === undefined) {
128
+ throw new Error(`kv unit '${this.descriptor.name}' declared no table '${table}'`);
129
+ }
130
+ return statements;
131
+ }
132
+ }
@@ -20,7 +20,7 @@ The Client uses these Host projections and mutations:
20
20
  - `archiveChannel` hides a Channel from every surface with its facts kept (no restore entry point yet).
21
21
  - `updateMember` commits Agent handle/description edits plus an optional per-Member model override.
22
22
  - `archiveMember` hides an Agent from every surface with its session log and private memory kept (no restore entry point yet).
23
- The Host Remote also exposes `threadObservations` and `changeAttention` for future UI and Agent workflows; the current Human Thread surface does not render these controls or observations. `changes` provides lightweight scoped invalidation: each request declares one `scope` (workspace, channel, thread, or presence) and only matching events wake its long-poll; a Thread read commits durably but wakes nobody and moves no version, because it changes no shared projection. Versions are per-scope cursors — a `presence` scope counts process-local presence edges while every other scope carries the durable ledger position of the newest shared-projection commit so a parked client is neither woken nor answered early by a change it does not subscribe to. The client shares one abortable long-poll per scope through `TeamChangeStream`, keeps each poll's cursor in the scope it came from, and re-anchors on any answered difference, so panels and pages never open parallel `changes` requests and a stale cursor cannot park a subscription blind; the poll is aborted when the last subscriber leaves. Opening a Task Thread issues one parallel round (`readThread`, bounded history, members, channel view) with no self-triggered second wave.
23
+ The Host Remote also exposes `threadObservations` and `changeAttention`; the current Human Thread surface does not render these controls or observations. `changes` is a scoped streaming Remote consumed through Harness `ctx.remote.$stream()`. Each page shares one logical subscription per scope and Harness multiplexes it over the page's shared WebSocket, so opening several Team pages does not reserve ordinary HTTP connections for notifications. Every opening/reconnection baseline triggers a Host reread, including unchanged versions; leaving the last subscriber cancels that scope. Opening a Thread performs parallel initial reads and may perform a baseline catch-up, but private read acknowledgments cause no shared notification loop. See [architecture](../../docs/architecture.md#host-authority) for the authoritative scope and recovery contract.
24
24
 
25
25
  Team mode, selected Workspace, and the last selected Channel or Thread are persisted in browser storage, so returning to Team restores the previous location. Attention, unread counts, revisions, observations, and Thread facts remain Host-owned. Durable mutations are refreshed from Host after commit or rejection.
26
26
 
@@ -20,7 +20,7 @@ Client 使用以下 Host 接口:
20
20
  - `archiveChannel`:把频道从所有面收起,事实保留(暂无恢复入口)。
21
21
  - `updateMember`:提交 Agent 名称/说明编辑,以及可选的成员级模型覆盖。
22
22
  - `archiveMember`:把 Agent 从所有面收起,会话日志与私有记忆保留(暂无恢复入口)。
23
- Host Remote 仍提供 `threadObservations` 和 `changeAttention`,供后续 UI 与 Agent 工作流使用;当前 Human Thread surface 不渲染这些控制或观察。`changes` 提供轻量的范围化变更通知:每个请求声明一个 `scope`(workspace、channel、thread presence),只有匹配的事件会唤醒对应 long-poll;Thread 读取会持久化提交,但既不唤醒任何 scope、也不推进任何 version,因为它不改变任何共享投影。version 是各 scope 自己的游标:`presence` scope 计进程内的 presence 边沿,其余 scope 报「最后一条改变共享投影的提交」在 ledger 上的 durable 位置。因此 parked client 不会被自己不订阅的变化唤醒,也不会被它提前应答。Client 通过 `TeamChangeStream` 在每个 scope 上共享一条可取消的 long-poll,每个 poll 的游标只留在签发它的 scope 内,任何被应答的差异都会重新锚定——面板与页面不会为同一 scope 并行发起 `changes` 请求,陈旧的游标也不会让订阅无声停靠;最后一个订阅者离开时轮询即被取消。打开 Task Thread 只发出一轮并行请求(`readThread`、有界历史、成员、频道视图),不会出现自触发的第二波请求。
23
+ Host Remote 仍提供 `threadObservations` 和 `changeAttention`;当前 Human Thread 界面不渲染这些控制或观察。`changes` 是按 scope 订阅的流式 Remote,通过 Harness `ctx.remote.$stream()` 消费。每个页面内同 scope 共享一个逻辑订阅,Harness 将其复用到该页面的共享 WebSocket,因此多个 Team 页面不会因通知长期占用普通 HTTP 连接。每次开场或重连基线都触发重新读取 Host,包括版本未变化的情况;最后一个订阅者离开时取消该 scope。打开 Thread 时并行完成首次读取,允许基线到达后补读,但私有已读确认不会形成共享通知循环。scope 与恢复契约见[架构文档](../../docs/architecture.zh.md#host-authority)。
24
24
 
25
25
  浏览器持久化 Team mode、当前 Workspace,以及最后选中的 Channel 或 Thread,返回 Team 时会恢复之前的位置。Attention、未读数量、revision、observations 和 Thread facts 始终由 Host 管理。持久化操作提交或拒绝后,Client 会重新读取 Host 投影。
26
26