dsh-draw 0.1.0 → 0.1.2

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.
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The session-event adaptive gate. `draw/generated` is declared only in this
3
+ * package's types, so it sits outside every host's `KNOWN_SESSION_EVENT_TYPES`:
4
+ * hosts whose session reader validates a static whitelist (rc.6/rc.7) refuse to
5
+ * reopen a session containing the event unless it carries the `ignorable`
6
+ * envelope — and those hosts' `Session.append` cannot stamp it. The gate
7
+ * therefore appends only when the type is host-known or a mount-time probe
8
+ * proves envelope support; otherwise the accounting payload goes to the
9
+ * in-memory fallback ledger (see session-events.ts) so quota keeps working for
10
+ * the live session without poisoning the log.
11
+ *
12
+ * @module dsh-draw/event-gate
13
+ */
14
+ /** One gate decision: whether to append, and whether to mark ignorable. */
15
+ export interface EventGateDecision {
16
+ /** Append the event to the session log. */
17
+ append: boolean;
18
+ /** Stamp the `ignorable` envelope (only meaningful when append). */
19
+ ignorable: boolean;
20
+ }
21
+ /** Decision function over session event types. */
22
+ export type EventGate = (type: string) => EventGateDecision;
23
+ /**
24
+ * Build the gate: host-known types append plainly; unknown types append with
25
+ * the ignorable envelope only when the host append honors it; everything else
26
+ * is skipped — an unknown, unmarked event would make the session refuse to
27
+ * load on restart under the rc.6/rc.7 static whitelist.
28
+ *
29
+ * @param knownTypes - the host's KNOWN_SESSION_EVENT_TYPES.
30
+ * @param ignorableAppend - whether the host append stamps the envelope.
31
+ * @returns the decision function.
32
+ */
33
+ export declare function makeEventGate(knownTypes: ReadonlySet<string>, ignorableAppend?: boolean): EventGate;
34
+ /**
35
+ * Probe whether the host `Session.append` stamps the ignorable envelope. Runs
36
+ * on a fully detached SessionStore — never host persistence — appending one
37
+ * probe event with `{ ignorable: true }` and reading the marker back. rc.6's
38
+ * append silently drops the unknown options key (probe false); every failure
39
+ * is contained as "unsupported".
40
+ *
41
+ * @returns whether the envelope is honored.
42
+ */
43
+ export declare function probeIgnorableAppend(): boolean;
44
+ /** The gate over the host's known types plus the probed envelope support. */
45
+ export declare function makeHostEventGate(): EventGate;
46
+ //# sourceMappingURL=event-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-gate.d.ts","sourceRoot":"","sources":["../../src/event-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC,2CAA2C;IAC3C,MAAM,EAAE,OAAO,CAAA;IACf,oEAAoE;IACpE,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,kDAAkD;AAClD,MAAM,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,iBAAiB,CAAA;AAE3D;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,eAAe,UAAQ,GAAG,SAAS,CAMjG;AAKD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAoB9C;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,IAAI,SAAS,CAE7C"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * The session-event adaptive gate. `draw/generated` is declared only in this
3
+ * package's types, so it sits outside every host's `KNOWN_SESSION_EVENT_TYPES`:
4
+ * hosts whose session reader validates a static whitelist (rc.6/rc.7) refuse to
5
+ * reopen a session containing the event unless it carries the `ignorable`
6
+ * envelope — and those hosts' `Session.append` cannot stamp it. The gate
7
+ * therefore appends only when the type is host-known or a mount-time probe
8
+ * proves envelope support; otherwise the accounting payload goes to the
9
+ * in-memory fallback ledger (see session-events.ts) so quota keeps working for
10
+ * the live session without poisoning the log.
11
+ *
12
+ * @module dsh-draw/event-gate
13
+ */
14
+ import { Context } from '@deepseek-ai/cordis';
15
+ import SessionStore, { KNOWN_SESSION_EVENT_TYPES } from '@deepseek-ai/dsh-session';
16
+ /**
17
+ * Build the gate: host-known types append plainly; unknown types append with
18
+ * the ignorable envelope only when the host append honors it; everything else
19
+ * is skipped — an unknown, unmarked event would make the session refuse to
20
+ * load on restart under the rc.6/rc.7 static whitelist.
21
+ *
22
+ * @param knownTypes - the host's KNOWN_SESSION_EVENT_TYPES.
23
+ * @param ignorableAppend - whether the host append stamps the envelope.
24
+ * @returns the decision function.
25
+ */
26
+ export function makeEventGate(knownTypes, ignorableAppend = false) {
27
+ return (type) => {
28
+ if (knownTypes.has(type))
29
+ return { append: true, ignorable: false };
30
+ if (ignorableAppend)
31
+ return { append: true, ignorable: true };
32
+ return { append: false, ignorable: false };
33
+ };
34
+ }
35
+ /**
36
+ * Probe whether the host `Session.append` stamps the ignorable envelope. Runs
37
+ * on a fully detached SessionStore — never host persistence — appending one
38
+ * probe event with `{ ignorable: true }` and reading the marker back. rc.6's
39
+ * append silently drops the unknown options key (probe false); every failure
40
+ * is contained as "unsupported".
41
+ *
42
+ * @returns whether the envelope is honored.
43
+ */
44
+ export function probeIgnorableAppend() {
45
+ try {
46
+ const store = new SessionStore(new Context());
47
+ const session = store.create();
48
+ const event = session.append('draw/generated', {
49
+ engine: 'probe',
50
+ model: 'probe',
51
+ source: 'tool',
52
+ prompt: 'probe',
53
+ size: 'auto',
54
+ quality: 'auto',
55
+ count: 0,
56
+ bytes: 0,
57
+ attachmentIds: [],
58
+ elapsedMs: 0,
59
+ }, { ignorable: true });
60
+ return event?.ignorable === true;
61
+ }
62
+ catch {
63
+ return false;
64
+ }
65
+ }
66
+ /** The gate over the host's known types plus the probed envelope support. */
67
+ export function makeHostEventGate() {
68
+ return makeEventGate(KNOWN_SESSION_EVENT_TYPES, probeIgnorableAppend());
69
+ }
@@ -14,23 +14,24 @@
14
14
  * @module dsh-draw
15
15
  */
16
16
  import type { Context } from '@deepseek-ai/cordis';
17
- import { Config } from './config.ts';
17
+ import { Config } from './config.js';
18
18
  export declare const name = "dsh-draw";
19
19
  /** Hard services: the tool registry every contribution lands in. */
20
20
  export declare const inject: string[];
21
- export { Config, resolveConfig, type Config as DrawConfig, type ResolvedConfig, DEFAULT_ENGINES, engineById } from './config.ts';
22
- export { EngineRouter, type AttemptView, type EngineStatus, type ProbeOutcome } from './router.ts';
23
- export { Drawer, type DrawImage, type DrawFailureReason, type DrawOutcome, type DrawOptions, type DrawSuccess } from './drawer.ts';
24
- export { imageGenerateTool } from './tool.ts';
25
- export { DrawService } from './service.ts';
26
- export { defaultHttpTransport, fusedSignal, type HttpTransport, type HttpRequest, type HttpResponse, HttpError } from './http.ts';
27
- export { callEngine, EngineCallError, type EngineDeps, type ProducedImage } from './engine.ts';
28
- export { translateRequest, normalizeRequest, type StandardImageRequest } from './translate.ts';
29
- export { quotaState, checkQuotaGenerations, checkQuotaBytes, type QuotaLimits, type QuotaState } from './quota.ts';
30
- export { sanitizeUrl, sanitizeText, sanitizeError, REDACTED } from './sanitize.ts';
31
- export { appendDrawGenerated, drawGeneratedEvents, type DrawGeneratedEvent } from './session-events.ts';
32
- export { PLUGIN_VERSION } from './version.ts';
33
- export { DRAW_INVOCATIONS, imageToWire, statusToView, probeToWire, type DrawStatusSnapshot } from './wire.ts';
21
+ export { Config, resolveConfig, type Config as DrawConfig, type ResolvedConfig, DEFAULT_ENGINES, engineById } from './config.js';
22
+ export { EngineRouter, type AttemptView, type EngineStatus, type ProbeOutcome } from './router.js';
23
+ export { Drawer, type DrawImage, type DrawFailureReason, type DrawOutcome, type DrawOptions, type DrawSuccess } from './drawer.js';
24
+ export { imageGenerateTool } from './tool.js';
25
+ export { DrawService } from './service.js';
26
+ export { defaultHttpTransport, fusedSignal, type HttpTransport, type HttpRequest, type HttpResponse, HttpError } from './http.js';
27
+ export { callEngine, EngineCallError, type EngineDeps, type ProducedImage } from './engine.js';
28
+ export { translateRequest, normalizeRequest, type StandardImageRequest } from './translate.js';
29
+ export { quotaState, checkQuotaGenerations, checkQuotaBytes, type QuotaLimits, type QuotaState } from './quota.js';
30
+ export { sanitizeUrl, sanitizeText, sanitizeError, REDACTED } from './sanitize.js';
31
+ export { appendDrawGenerated, commitDrawGenerated, drawGeneratedEvents, fallbackDrawGeneratedEvents, type DrawGeneratedEvent } from './session-events.js';
32
+ export { makeEventGate, makeHostEventGate, probeIgnorableAppend, type EventGate, type EventGateDecision } from './event-gate.js';
33
+ export { PLUGIN_VERSION } from './version.js';
34
+ export { DRAW_INVOCATIONS, imageToWire, statusToView, probeToWire, type DrawStatusSnapshot } from './wire.js';
34
35
  /**
35
36
  * Mount the plugin: router, drawer, the `image_generate` tool, and the `draw`
36
37
  * Remote service. Every registration is an effect on this fiber, so
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAMlD,OAAO,EAAE,MAAM,EAAiB,MAAM,aAAa,CAAA;AAOnD,eAAO,MAAM,IAAI,aAAa,CAAA;AAE9B,oEAAoE;AACpE,eAAO,MAAM,MAAM,UAAY,CAAA;AAE/B,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,MAAM,IAAI,UAAU,EAAE,KAAK,cAAc,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAChI,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AAClG,OAAO,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAClI,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACjI,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAA;AAC9F,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAC9F,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,eAAe,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAA;AAClH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAClF,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACvG,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAK7G;;;;;;;GAOG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiCvE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAMlD,OAAO,EAAE,MAAM,EAAiB,MAAM,aAAa,CAAA;AAQnD,eAAO,MAAM,IAAI,aAAa,CAAA;AAE9B,oEAAoE;AACpE,eAAO,MAAM,MAAM,UAAY,CAAA;AAE/B,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,MAAM,IAAI,UAAU,EAAE,KAAK,cAAc,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAChI,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AAClG,OAAO,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAClI,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACjI,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAA;AAC9F,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAC9F,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,eAAe,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAA;AAClH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAClF,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACzJ,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAChI,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAK7G;;;;;;;GAOG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCvE"}
@@ -16,6 +16,7 @@
16
16
  import { credentialRef } from '@deepseek-ai/dsh-credentials';
17
17
  import { resolveConfig } from './config.js';
18
18
  import { Drawer } from './drawer.js';
19
+ import { makeHostEventGate } from './event-gate.js';
19
20
  import { defaultHttpTransport } from './http.js';
20
21
  import { EngineRouter } from './router.js';
21
22
  import { DrawService } from './service.js';
@@ -33,7 +34,8 @@ export { callEngine, EngineCallError } from './engine.js';
33
34
  export { translateRequest, normalizeRequest } from './translate.js';
34
35
  export { quotaState, checkQuotaGenerations, checkQuotaBytes } from './quota.js';
35
36
  export { sanitizeUrl, sanitizeText, sanitizeError, REDACTED } from './sanitize.js';
36
- export { appendDrawGenerated, drawGeneratedEvents } from './session-events.js';
37
+ export { appendDrawGenerated, commitDrawGenerated, drawGeneratedEvents, fallbackDrawGeneratedEvents } from './session-events.js';
38
+ export { makeEventGate, makeHostEventGate, probeIgnorableAppend } from './event-gate.js';
37
39
  export { PLUGIN_VERSION } from './version.js';
38
40
  export { DRAW_INVOCATIONS, imageToWire, statusToView, probeToWire } from './wire.js';
39
41
  /** Response byte ceiling: one engine call may carry several full-size images. */
@@ -58,6 +60,9 @@ export async function apply(ctx, config) {
58
60
  cooldownMs: resolved.cooldownMs,
59
61
  });
60
62
  const credentials = () => ctx.get('credentials');
63
+ // The draw/generated event type is declared only by this package: append it
64
+ // only when the host knows the type or honors the ignorable envelope;
65
+ // otherwise the accounting payload rides the in-memory fallback ledger.
61
66
  const drawer = new Drawer(resolved, router, {
62
67
  engine: {
63
68
  transport,
@@ -71,7 +76,7 @@ export async function apply(ctx, config) {
71
76
  },
72
77
  attachments: () => ctx.get('attachments'),
73
78
  sessions: () => ctx.get('sessions'),
74
- });
79
+ }, { gate: makeHostEventGate(), warn: (message) => logger.warn(message) });
75
80
  ctx.effect(() => ctx.tools.register(imageGenerateTool(drawer, resolved)), 'dsh-draw: image_generate tool');
76
81
  await ctx.plugin(DrawService, { config: resolved, router, drawer, credentials: credentials() });
77
82
  logger.info(`image generation enabled: ${resolved.engines.map(engine => engine.id).join(', ')} (preferred ${resolved.defaultEngine})`);
@@ -1,8 +1,12 @@
1
1
  /**
2
2
  * Per-session quota accounting. The durable source of truth is the session
3
- * log: every completed generation appends one `draw/generated` event, and
4
- * quota is computed by folding those events, so usage survives restart and
5
- * fork and cannot drift from what the log records.
3
+ * log: every completed generation commits one `draw/generated` event, and
4
+ * quota folds those events, so usage survives restart and fork and cannot
5
+ * drift from what the log records. On hosts that cannot carry the event
6
+ * safely (rc.6/rc.7 static whitelist without the ignorable envelope), commits
7
+ * land in the in-memory fallback ledger instead and quota folds both sources —
8
+ * live-session accounting stays exact; cross-restart durability resumes when
9
+ * the host gains a plugin event surface.
6
10
  *
7
11
  * @module dsh-draw/quota
8
12
  */
@@ -40,9 +44,11 @@ export interface QuotaAllowance {
40
44
  /** Quota decision. */
41
45
  export type QuotaCheck = QuotaAllowance | QuotaDenial;
42
46
  /**
43
- * Fold one session's `draw/generated` events into current usage.
47
+ * Fold one session's `draw/generated` events into current usage: logged events
48
+ * first, then the in-memory fallback ledger (disjoint — a commit lands in
49
+ * exactly one of them).
44
50
  *
45
- * @param session - session whose log is folded.
51
+ * @param session - session whose log and ledger are folded.
46
52
  * @returns generation and byte totals.
47
53
  */
48
54
  export declare function quotaState(session: Session): QuotaState;
@@ -1 +1 @@
1
- {"version":3,"file":"quota.d.ts","sourceRoot":"","sources":["../../src/quota.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAA;AAGvD,0BAA0B;AAC1B,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,cAAc,EAAE,MAAM,CAAA;IACtB,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,kDAAkD;AAClD,MAAM,WAAW,UAAU;IACzB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAA;IACnB,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAA;CACd;AAED,qEAAqE;AACrE,MAAM,WAAW,WAAW;IAC1B,oBAAoB;IACpB,OAAO,EAAE,KAAK,CAAA;IACd,mCAAmC;IACnC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAA;IAC/B,8BAA8B;IAC9B,KAAK,EAAE,UAAU,CAAA;CAClB;AAED,qDAAqD;AACrD,MAAM,WAAW,cAAc;IAC7B,oBAAoB;IACpB,OAAO,EAAE,IAAI,CAAA;IACb,8BAA8B;IAC9B,KAAK,EAAE,UAAU,CAAA;CAClB;AAED,sBAAsB;AACtB,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,WAAW,CAAA;AAErD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CAQvD;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,GAAG,UAAU,CAMvF;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,GAAG,UAAU,CAMxG"}
1
+ {"version":3,"file":"quota.d.ts","sourceRoot":"","sources":["../../src/quota.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAA;AAGvD,0BAA0B;AAC1B,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,cAAc,EAAE,MAAM,CAAA;IACtB,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,kDAAkD;AAClD,MAAM,WAAW,UAAU;IACzB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAA;IACnB,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAA;CACd;AAED,qEAAqE;AACrE,MAAM,WAAW,WAAW;IAC1B,oBAAoB;IACpB,OAAO,EAAE,KAAK,CAAA;IACd,mCAAmC;IACnC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAA;IAC/B,8BAA8B;IAC9B,KAAK,EAAE,UAAU,CAAA;CAClB;AAED,qDAAqD;AACrD,MAAM,WAAW,cAAc;IAC7B,oBAAoB;IACpB,OAAO,EAAE,IAAI,CAAA;IACb,8BAA8B;IAC9B,KAAK,EAAE,UAAU,CAAA;CAClB;AAED,sBAAsB;AACtB,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,WAAW,CAAA;AAErD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CAYvD;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,GAAG,UAAU,CAMvF;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,GAAG,UAAU,CAMxG"}
@@ -1,16 +1,22 @@
1
1
  /**
2
2
  * Per-session quota accounting. The durable source of truth is the session
3
- * log: every completed generation appends one `draw/generated` event, and
4
- * quota is computed by folding those events, so usage survives restart and
5
- * fork and cannot drift from what the log records.
3
+ * log: every completed generation commits one `draw/generated` event, and
4
+ * quota folds those events, so usage survives restart and fork and cannot
5
+ * drift from what the log records. On hosts that cannot carry the event
6
+ * safely (rc.6/rc.7 static whitelist without the ignorable envelope), commits
7
+ * land in the in-memory fallback ledger instead and quota folds both sources —
8
+ * live-session accounting stays exact; cross-restart durability resumes when
9
+ * the host gains a plugin event surface.
6
10
  *
7
11
  * @module dsh-draw/quota
8
12
  */
9
- import { drawGeneratedEvents } from './session-events.js';
13
+ import { drawGeneratedEvents, fallbackDrawGeneratedEvents } from './session-events.js';
10
14
  /**
11
- * Fold one session's `draw/generated` events into current usage.
15
+ * Fold one session's `draw/generated` events into current usage: logged events
16
+ * first, then the in-memory fallback ledger (disjoint — a commit lands in
17
+ * exactly one of them).
12
18
  *
13
- * @param session - session whose log is folded.
19
+ * @param session - session whose log and ledger are folded.
14
20
  * @returns generation and byte totals.
15
21
  */
16
22
  export function quotaState(session) {
@@ -20,6 +26,10 @@ export function quotaState(session) {
20
26
  generations += 1;
21
27
  bytes += event.data.bytes;
22
28
  }
29
+ for (const event of fallbackDrawGeneratedEvents(session)) {
30
+ generations += 1;
31
+ bytes += event.bytes;
32
+ }
23
33
  return { generations, bytes };
24
34
  }
25
35
  /**
@@ -7,9 +7,9 @@
7
7
  *
8
8
  * @module dsh-draw/router
9
9
  */
10
- import type { ResolvedConfig, ResolvedEngineConfig } from './config.ts';
11
- import { type EngineDeps, type ProducedImage } from './engine.ts';
12
- import { type StandardImageRequest } from './translate.ts';
10
+ import type { ResolvedConfig, ResolvedEngineConfig } from './config.js';
11
+ import { type EngineDeps, type ProducedImage } from './engine.js';
12
+ import { type StandardImageRequest } from './translate.js';
13
13
  /** One recorded attempt against one engine, success or failure. */
14
14
  export interface AttemptView {
15
15
  /** Engine id. */
@@ -11,10 +11,10 @@
11
11
  import type { Context } from '@deepseek-ai/cordis';
12
12
  import { type CredentialProvider } from '@deepseek-ai/dsh-credentials';
13
13
  import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
14
- import type { ResolvedConfig } from './config.ts';
15
- import type { Drawer } from './drawer.ts';
16
- import type { EngineRouter } from './router.ts';
17
- import { type CredentialActionResult, type DrawProbeResult, type DrawRegenerateResult, type DrawStatusSnapshot } from './wire.ts';
14
+ import type { ResolvedConfig } from './config.js';
15
+ import type { Drawer } from './drawer.js';
16
+ import type { EngineRouter } from './router.js';
17
+ import { type CredentialActionResult, type DrawProbeResult, type DrawRegenerateResult, type DrawStatusSnapshot } from './wire.js';
18
18
  declare module '@deepseek-ai/cordis' {
19
19
  interface Context {
20
20
  /** Image-generation host service (this package). */
@@ -6,9 +6,17 @@
6
6
  * output of the call ride the loop-owned `tool/call` and `tool/result`
7
7
  * events; this event carries the accounting facts those events do not.
8
8
  *
9
+ * The event type is declared only here, so plugin paths append it through
10
+ * {@link commitDrawGenerated}: the adaptive gate (event-gate.ts) appends only
11
+ * when the host knows the type or honors the `ignorable` envelope, and
12
+ * otherwise records the payload in the in-memory fallback ledger (live-session
13
+ * quota keeps working; the durable trail resumes once the host gains a plugin
14
+ * event surface).
15
+ *
9
16
  * @module dsh-draw/session-events
10
17
  */
11
18
  import type { Session } from '@deepseek-ai/dsh-session';
19
+ import type { EventGate } from './event-gate.js';
12
20
  declare module '@deepseek-ai/dsh-session/types' {
13
21
  interface SessionEventMap {
14
22
  /**
@@ -42,7 +50,9 @@ export interface DrawGeneratedEvent {
42
50
  elapsedMs: number;
43
51
  }
44
52
  /**
45
- * Append one `draw/generated` event to a session.
53
+ * Append one `draw/generated` event to a session, ungated. Plugin paths use
54
+ * {@link commitDrawGenerated} instead; this raw form remains for tests and
55
+ * hosts that know the type.
46
56
  *
47
57
  * @param session - owning session.
48
58
  * @param event - accounting payload.
@@ -55,6 +65,27 @@ export declare function appendDrawGenerated(session: Session, event: DrawGenerat
55
65
  data: DrawGeneratedEvent;
56
66
  ignorable?: true;
57
67
  } & object;
68
+ /**
69
+ * Commit one completed generation: append the `draw/generated` event when the
70
+ * gate allows (with the ignorable envelope when required), otherwise record
71
+ * the payload in the in-memory fallback ledger. A failed append degrades to
72
+ * the same ledger and never breaks the draw.
73
+ *
74
+ * @param session - owning session.
75
+ * @param event - accounting payload.
76
+ * @param gate - the adaptive gate (event-gate.ts).
77
+ * @param warn - log warning sink for append failures.
78
+ */
79
+ export declare function commitDrawGenerated(session: Session, event: DrawGeneratedEvent, gate: EventGate, warn: (message: string) => void): void;
80
+ /**
81
+ * The in-memory fallback records of a session, in commit order. Populated
82
+ * only while the host cannot carry `draw/generated` in the log; quota folds
83
+ * these on top of the logged events.
84
+ *
85
+ * @param session - session whose ledger is read.
86
+ * @returns the fallback payloads.
87
+ */
88
+ export declare function fallbackDrawGeneratedEvents(session: Session): readonly DrawGeneratedEvent[];
58
89
  /**
59
90
  * Collect the `draw/generated` events of a session in log order. Declaration
60
91
  * merging narrows `event.type === 'draw/generated'` to the payload type.
@@ -1 +1 @@
1
- {"version":3,"file":"session-events.d.ts","sourceRoot":"","sources":["../../src/session-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAA;AAEvD,OAAO,QAAQ,gCAAgC,CAAC;IAC9C,UAAU,eAAe;QACvB;;;WAGG;QACH,gBAAgB,EAAE,kBAAkB,CAAA;KACrC;CACF;AAED,qDAAqD;AACrD,MAAM,WAAW,kBAAkB;IACjC,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAA;IACd,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,MAAM,EAAE,MAAM,GAAG,YAAY,CAAA;IAC7B,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAA;IACd,qDAAqD;IACrD,IAAI,EAAE,QAAQ,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,CAAA;IAClD,6CAA6C;IAC7C,OAAO,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAA;IAC3C,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,aAAa,EAAE,SAAS,MAAM,EAAE,CAAA;IAChC,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB;;;;;;WAI9E;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,OAAO,0BAA0B,EAAE,YAAY,CAAC,gBAAgB,CAAC,EAAE,CAElI"}
1
+ {"version":3,"file":"session-events.d.ts","sourceRoot":"","sources":["../../src/session-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAA;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,QAAQ,gCAAgC,CAAC;IAC9C,UAAU,eAAe;QACvB;;;WAGG;QACH,gBAAgB,EAAE,kBAAkB,CAAA;KACrC;CACF;AAED,qDAAqD;AACrD,MAAM,WAAW,kBAAkB;IACjC,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAA;IACd,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,MAAM,EAAE,MAAM,GAAG,YAAY,CAAA;IAC7B,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAA;IACd,qDAAqD;IACrD,IAAI,EAAE,QAAQ,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,CAAA;IAClD,6CAA6C;IAC7C,OAAO,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAA;IAC3C,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,aAAa,EAAE,SAAS,MAAM,EAAE,CAAA;IAChC,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB;;;;;;WAE9E;AAYD;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAiBvI;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,kBAAkB,EAAE,CAE3F;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,OAAO,0BAA0B,EAAE,YAAY,CAAC,gBAAgB,CAAC,EAAE,CAElI"}
@@ -6,20 +6,76 @@
6
6
  * output of the call ride the loop-owned `tool/call` and `tool/result`
7
7
  * events; this event carries the accounting facts those events do not.
8
8
  *
9
+ * The event type is declared only here, so plugin paths append it through
10
+ * {@link commitDrawGenerated}: the adaptive gate (event-gate.ts) appends only
11
+ * when the host knows the type or honors the `ignorable` envelope, and
12
+ * otherwise records the payload in the in-memory fallback ledger (live-session
13
+ * quota keeps working; the durable trail resumes once the host gains a plugin
14
+ * event surface).
15
+ *
9
16
  * @module dsh-draw/session-events
10
17
  */
11
18
  /**
12
- * Append one `draw/generated` event to a session.
19
+ * Append one `draw/generated` event to a session, ungated. Plugin paths use
20
+ * {@link commitDrawGenerated} instead; this raw form remains for tests and
21
+ * hosts that know the type.
13
22
  *
14
23
  * @param session - owning session.
15
24
  * @param event - accounting payload.
16
25
  * @returns the logged event.
17
26
  */
18
27
  export function appendDrawGenerated(session, event) {
19
- // Two-argument append: the pinned 0.1.0-rc.6 peers have no append-envelope
20
- // option; the two-argument form typechecks against rc.6 and newer builds.
21
28
  return session.append('draw/generated', event);
22
29
  }
30
+ /**
31
+ * In-memory accounting ledger for hosts whose session log cannot carry
32
+ * `draw/generated` safely (rc.6/rc.7 static whitelist, no ignorable envelope).
33
+ * Keyed by Session identity: the entries live exactly as long as the session.
34
+ */
35
+ const fallbackLedger = new WeakMap();
36
+ /**
37
+ * Commit one completed generation: append the `draw/generated` event when the
38
+ * gate allows (with the ignorable envelope when required), otherwise record
39
+ * the payload in the in-memory fallback ledger. A failed append degrades to
40
+ * the same ledger and never breaks the draw.
41
+ *
42
+ * @param session - owning session.
43
+ * @param event - accounting payload.
44
+ * @param gate - the adaptive gate (event-gate.ts).
45
+ * @param warn - log warning sink for append failures.
46
+ */
47
+ export function commitDrawGenerated(session, event, gate, warn) {
48
+ const decision = gate('draw/generated');
49
+ if (decision.append) {
50
+ try {
51
+ if (decision.ignorable) {
52
+ ;
53
+ session.append('draw/generated', event, { ignorable: true });
54
+ }
55
+ else {
56
+ session.append('draw/generated', event);
57
+ }
58
+ return;
59
+ }
60
+ catch (error) {
61
+ warn(`draw session event append failed: ${error instanceof Error ? error.message : String(error)}`);
62
+ }
63
+ }
64
+ const list = fallbackLedger.get(session) ?? [];
65
+ list.push(event);
66
+ fallbackLedger.set(session, list);
67
+ }
68
+ /**
69
+ * The in-memory fallback records of a session, in commit order. Populated
70
+ * only while the host cannot carry `draw/generated` in the log; quota folds
71
+ * these on top of the logged events.
72
+ *
73
+ * @param session - session whose ledger is read.
74
+ * @returns the fallback payloads.
75
+ */
76
+ export function fallbackDrawGeneratedEvents(session) {
77
+ return fallbackLedger.get(session) ?? [];
78
+ }
23
79
  /**
24
80
  * Collect the `draw/generated` events of a session in log order. Declaration
25
81
  * merging narrows `event.type === 'draw/generated'` to the payload type.
@@ -8,8 +8,8 @@
8
8
  *
9
9
  * @module dsh-draw/tool
10
10
  */
11
- import type { ResolvedConfig } from './config.ts';
12
- import type { Drawer } from './drawer.ts';
11
+ import type { ResolvedConfig } from './config.js';
12
+ import type { Drawer } from './drawer.js';
13
13
  /** Tool argument surface (validated by the schema; the drawer re-normalizes). */
14
14
  export interface ImageGenerateArgs {
15
15
  prompt: string;
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @module dsh-draw/translate
7
7
  */
8
- import type { ResolvedEngineConfig, StandardSize } from './config.ts';
8
+ import type { ResolvedEngineConfig, StandardSize } from './config.js';
9
9
  /** Standard quality vocabulary; `auto` means "leave the engine default". */
10
10
  export type StandardQuality = 'low' | 'medium' | 'high' | 'auto';
11
11
  /** Standard style vocabulary (engines that lack styles drop it). */
@@ -6,5 +6,5 @@
6
6
  * @module dsh-draw/version
7
7
  */
8
8
  /** Plugin version; must equal the `version` field in `package.json`. */
9
- export declare const PLUGIN_VERSION = "0.1.0";
9
+ export declare const PLUGIN_VERSION = "0.1.2";
10
10
  //# sourceMappingURL=version.d.ts.map
@@ -6,4 +6,4 @@
6
6
  * @module dsh-draw/version
7
7
  */
8
8
  /** Plugin version; must equal the `version` field in `package.json`. */
9
- export const PLUGIN_VERSION = '0.1.0';
9
+ export const PLUGIN_VERSION = '0.1.2';
@@ -9,9 +9,9 @@
9
9
  * @module dsh-draw/wire
10
10
  */
11
11
  import { z } from 'zod';
12
- import type { AttemptView, EngineStatus, ProbeOutcome } from './router.ts';
13
- import type { QuotaLimits, QuotaState } from './quota.ts';
14
- import type { DrawImage } from './drawer.ts';
12
+ import type { AttemptView, EngineStatus, ProbeOutcome } from './router.js';
13
+ import type { QuotaLimits, QuotaState } from './quota.js';
14
+ import type { DrawImage } from './drawer.js';
15
15
  /** Credential facts for one engine, safe for configuration UIs — never the value. */
16
16
  export interface EngineCredentialView {
17
17
  /** Whether the reference currently resolves to a value. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-draw",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Unified static-image generation router for DeepSeek Harness: one image_generate tool with standard parameters, config-driven OpenAI-compatible engine routing (OpenAI Images, Zhipu CogView, and any compatible endpoint) with health-aware fallback, durable workspace attachment results, per-session quota accounting, an in-conversation result card, and a Plugins settings panel that stores API keys as credential references.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -89,9 +89,7 @@
89
89
  "attachments:write"
90
90
  ],
91
91
  "compatibility": {
92
- "dshVersions": [
93
- "0.1.0-rc.6"
94
- ]
92
+ "dshVersions": ["0.1.1-rc.2"]
95
93
  },
96
94
  "capability": {
97
95
  "id": "image-generate-tool",
@@ -124,14 +122,14 @@
124
122
  "packageManager": "pnpm@11.7.0",
125
123
  "peerDependencies": {
126
124
  "@deepseek-ai/cordis": "^4.0.1",
127
- "@deepseek-ai/schemastery": "^3.18.0",
128
- "@deepseek-ai/dsh-attachment": "0.1.0-rc.6",
129
- "@deepseek-ai/dsh-brand": "0.1.0-rc.6",
130
- "@deepseek-ai/dsh-credentials": "0.1.0-rc.6",
131
- "@deepseek-ai/dsh-llm": "0.1.0-rc.6",
132
- "@deepseek-ai/dsh-session": "0.1.0-rc.6",
133
- "@deepseek-ai/dsh-tools": "0.1.0-rc.6",
134
- "@deepseek-ai/dsh-typert-protocol": "0.1.0-rc.6"
125
+ "@deepseek-ai/dsh-attachment": ">=0.1.0-rc.8 <0.2.0",
126
+ "@deepseek-ai/dsh-brand": ">=0.1.0-rc.8 <0.2.0",
127
+ "@deepseek-ai/dsh-credentials": ">=0.1.0-rc.8 <0.2.0",
128
+ "@deepseek-ai/dsh-llm": ">=0.1.0-rc.8 <0.2.0",
129
+ "@deepseek-ai/dsh-session": ">=0.1.0-rc.8 <0.2.0",
130
+ "@deepseek-ai/dsh-tools": ">=0.1.0-rc.8 <0.2.0",
131
+ "@deepseek-ai/dsh-typert-protocol": ">=0.1.0-rc.8 <0.2.0",
132
+ "@deepseek-ai/schemastery": "^3.18.0"
135
133
  },
136
134
  "dependencies": {
137
135
  "tsdown": "^0.22.14",
@@ -140,27 +138,31 @@
140
138
  },
141
139
  "devDependencies": {
142
140
  "@deepseek-ai/cordis": "^4.0.1",
141
+ "@deepseek-ai/cordis-plugin-include": "1.0.6",
142
+ "@deepseek-ai/cordis-plugin-loader": "1.0.2",
143
+ "@deepseek-ai/dsh-agent": "0.1.1-rc.2",
144
+ "@deepseek-ai/dsh-api-remotes": "0.1.1-rc.2",
145
+ "@deepseek-ai/dsh-attachment": "0.1.1-rc.2",
146
+ "@deepseek-ai/dsh-attachment-local": "0.1.1-rc.2",
147
+ "@deepseek-ai/dsh-brand": "0.1.1-rc.2",
148
+ "@deepseek-ai/dsh-client-connection": "0.1.1-rc.2",
149
+ "@deepseek-ai/dsh-client-locale": "0.1.1-rc.2",
150
+ "@deepseek-ai/dsh-client-runtime": "0.1.1-rc.2",
151
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.1-rc.2",
152
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.1-rc.2",
153
+ "@deepseek-ai/dsh-client-ui-tool": "0.1.1-rc.2",
154
+ "@deepseek-ai/dsh-credentials": "0.1.1-rc.2",
155
+ "@deepseek-ai/dsh-credentials-local": "0.1.1-rc.2",
156
+ "@deepseek-ai/dsh-llm": "0.1.1-rc.2",
157
+ "@deepseek-ai/dsh-session": "0.1.1-rc.2",
158
+ "@deepseek-ai/dsh-tools": "0.1.1-rc.2",
159
+ "@deepseek-ai/dsh-typert-protocol": "0.1.1-rc.2",
143
160
  "@deepseek-ai/schemastery": "^3.18.0",
144
- "@deepseek-ai/dsh-agent": "0.1.0-rc.6",
145
- "@deepseek-ai/dsh-api-remotes": "0.1.0-rc.6",
146
- "@deepseek-ai/dsh-attachment": "0.1.0-rc.6",
147
- "@deepseek-ai/dsh-attachment-local": "0.1.0-rc.6",
148
- "@deepseek-ai/dsh-brand": "0.1.0-rc.6",
149
- "@deepseek-ai/dsh-client-connection": "0.1.0-rc.6",
150
- "@deepseek-ai/dsh-client-locale": "0.1.0-rc.6",
151
- "@deepseek-ai/dsh-client-runtime": "0.1.0-rc.6",
152
- "@deepseek-ai/dsh-client-ui-settings": "0.1.0-rc.6",
153
- "@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.6",
154
- "@deepseek-ai/dsh-client-ui-tool": "0.1.0-rc.6",
155
- "@deepseek-ai/dsh-credentials": "0.1.0-rc.6",
156
- "@deepseek-ai/dsh-credentials-local": "0.1.0-rc.6",
157
- "@deepseek-ai/dsh-llm": "0.1.0-rc.6",
158
- "@deepseek-ai/dsh-session": "0.1.0-rc.6",
159
- "@deepseek-ai/dsh-tools": "0.1.0-rc.6",
160
- "@deepseek-ai/dsh-typert-protocol": "0.1.0-rc.6",
161
161
  "@types/node": "^22.19.0",
162
162
  "@types/react": "18.3.31",
163
+ "@vitest/coverage-v8": "4.1.10",
163
164
  "jsdom": "^30.0.1",
165
+ "oxlint": "0.18.1",
164
166
  "react": "18.3.1",
165
167
  "react-dom": "18.3.1",
166
168
  "vitest": "^4.1.10"
@@ -171,6 +173,9 @@
171
173
  "typecheck:ci": "tsc -p tsconfig.ci.json --noEmit",
172
174
  "test": "vitest run",
173
175
  "test:watch": "vitest",
176
+ "test:coverage": "vitest run --coverage",
177
+ "lint": "oxlint",
178
+ "check:readmes": "node scripts/check-readme-sync.mjs",
174
179
  "prepare": "node scripts/prepare.mjs",
175
180
  "verify:self-contained": "node scripts/verify-self-contained.mjs",
176
181
  "verify:artifacts": "node scripts/verify-artifacts.mjs"