@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.
- package/README.md +2 -0
- package/README.zh.md +2 -0
- package/cordis.patch.yml +7 -3
- package/package.json +14 -2
- package/packages/agent-team/README.md +2 -2
- package/packages/agent-team/README.zh.md +2 -2
- package/packages/agent-team/lib/index.js +151 -69
- package/packages/agent-team/lib/ledger.js +249 -31
- package/packages/agent-team/lib/member-context.js +18 -1
- package/packages/agent-team/lib/member-runtime.js +5 -4
- package/packages/agent-team/lib/mentions.js +48 -24
- package/packages/agent-team/lib/spec.js +19 -0
- package/packages/agent-team/lib/typert.host.js +187 -41
- package/packages/agent-team/lib/typert.remote-client.d.ts +7 -3
- package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
- package/packages/agent-team/lib/typert.remote-client.js +132 -27
- package/packages/agent-team/lib/types/index.d.ts +32 -7
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/ledger.d.ts +46 -2
- package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-context.d.ts +6 -0
- package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
- package/packages/agent-team/lib/types/mentions.d.ts +20 -7
- package/packages/agent-team/lib/types/mentions.d.ts.map +1 -1
- package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/entities.d.ts +3 -2
- package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/operations.d.ts +37 -1
- package/packages/agent-team/lib/types/types/operations.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/requests-results.d.ts +50 -10
- package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
- package/packages/agent-team/lib/types/vendor/storage-sqlite/index.d.ts +87 -0
- package/packages/agent-team/lib/types/vendor/storage-sqlite/index.d.ts.map +1 -0
- package/packages/agent-team/lib/types/vendor/storage-sqlite/schema.d.ts +59 -0
- package/packages/agent-team/lib/types/vendor/storage-sqlite/schema.d.ts.map +1 -0
- package/packages/agent-team/lib/types/vendor/storage-sqlite/unit.d.ts +57 -0
- package/packages/agent-team/lib/types/vendor/storage-sqlite/unit.d.ts.map +1 -0
- package/packages/agent-team/lib/vendor/storage-sqlite/index.js +139 -0
- package/packages/agent-team/lib/vendor/storage-sqlite/schema.js +120 -0
- package/packages/agent-team/lib/vendor/storage-sqlite/unit.js +132 -0
- package/packages/client-agent-team/README.md +1 -1
- package/packages/client-agent-team/README.zh.md +1 -1
- package/packages/client-agent-team/lib/client.js +603 -343
- package/packages/client-agent-team/lib/client.js.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentImport.d.ts +12 -0
- package/packages/client-agent-team/lib/types/client/TeamAgentImport.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamAgentImport.js +69 -0
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +3 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +29 -17
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +19 -44
- package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts +2 -2
- package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamInboxPage.js +15 -14
- package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +15 -29
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +2 -2
- package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/index.js +8 -1
- package/packages/client-agent-team/lib/types/client/locales.d.ts +18 -0
- package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/locales.js +18 -0
- package/packages/client-agent-team/lib/types/client/slots.d.ts +3 -1
- package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-changes.d.ts +15 -31
- package/packages/client-agent-team/lib/types/client/team-changes.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-changes.js +82 -111
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +13 -11
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-formatters.js +29 -35
- package/packages/tool-agent-team/README.md +1 -1
- package/packages/tool-agent-team/README.zh.md +1 -1
- package/packages/tool-agent-team/lib/host-access.js +19 -0
- package/packages/tool-agent-team/lib/index.js +42 -33
- package/packages/tool-agent-team/lib/types/host-access.d.ts +10 -0
- package/packages/tool-agent-team/lib/types/host-access.d.ts.map +1 -1
- package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts +0 -41
- package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts.map +0 -1
- 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
|
|
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
|
|
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
|
|