@astrosheep/square 0.3.95 → 0.3.97

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 (51) hide show
  1. package/dist/artifact.d.ts +1 -1
  2. package/dist/artifact.js +11 -17
  3. package/dist/attention-presentation.d.ts +5 -4
  4. package/dist/attention-presentation.js +18 -8
  5. package/dist/boundary-presentation.js +31 -26
  6. package/dist/channel.d.ts +74 -0
  7. package/dist/channel.js +92 -0
  8. package/dist/claude-delivery.js +3 -9
  9. package/dist/cli/observation-commands.js +2 -2
  10. package/dist/codex-hook.d.ts +0 -2
  11. package/dist/codex-hook.js +0 -1
  12. package/dist/decisions.d.ts +2 -11
  13. package/dist/decisions.js +3 -8
  14. package/dist/delivery-health.js +3 -13
  15. package/dist/delivery.d.ts +1 -11
  16. package/dist/delivery.js +2 -9
  17. package/dist/identity.d.ts +0 -1
  18. package/dist/identity.js +0 -1
  19. package/dist/inbox.js +11 -7
  20. package/dist/mcp.js +65 -63
  21. package/dist/model.d.ts +6 -0
  22. package/dist/model.js +19 -0
  23. package/dist/notifications.d.ts +1 -1
  24. package/dist/ports.d.ts +5 -5
  25. package/dist/presentation-operations.d.ts +1 -0
  26. package/dist/sqlite-square.d.ts +3 -2
  27. package/dist/sqlite-square.js +91 -9
  28. package/dist/square-core.d.ts +1 -0
  29. package/dist/square-domain.js +22 -7
  30. package/dist/square-facade.d.ts +36 -10
  31. package/dist/square-sqlite.js +33 -4
  32. package/dist/square-tools.d.ts +1 -0
  33. package/dist/square-tools.js +9 -1
  34. package/dist/views.d.ts +1 -2
  35. package/dist/views.js +1 -9
  36. package/guides/operation-cancellation.md +5 -1
  37. package/guides/primitive-status-consolidation.md +1 -1
  38. package/guides/receive-primitive.md +4 -2
  39. package/guides/row-runtime-delivery-evidence.md +3 -3
  40. package/guides/row-storage-design.md +5 -1
  41. package/guides/square-api.md +63 -7
  42. package/package.json +5 -2
  43. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  44. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  45. package/plugins/codex/hooks/hooks.json +6 -6
  46. package/plugins/paseo/client/marble.ts +7 -0
  47. package/plugins/paseo/client/markdown.tsx +65 -9
  48. package/plugins/paseo/client/palette.ts +43 -20
  49. package/plugins/paseo/client/square-message.tsx +57 -48
  50. package/plugins/paseo/shared/message.ts +1 -2
  51. package/plugins/pi/square-pi.js +6 -4
@@ -1,10 +1,10 @@
1
1
  import type { DeliveryDeferral, MetadataMatch } from './delivery-deferral.js';
2
2
  export type { DeliveryDeferral, MetadataMatch, MetadataScalar } from './delivery-deferral.js';
3
3
  import type { ActivityId, Perception, Reach } from './square-core.js';
4
- import type { Owner, StoredAct, WakeAddress, ActivityMetadata, DirectedNotificationRoute } from './model.js';
4
+ import type { Owner, StoredAct, WakeAddress, ActivityMetadata, ActivityDelivery, DirectedNotificationRoute } from './model.js';
5
5
  import type { WakeTransportPort, SquareResourceOwner } from './ports.js';
6
6
  export type { ActivityId, Perception, Reach } from './square-core.js';
7
- export type { Owner, WakeAddress, ActivityMetadata, JsonValue, DirectedNotificationRoute } from './model.js';
7
+ export type { Owner, WakeAddress, ActivityMetadata, ActivityDelivery, JsonValue, DirectedNotificationRoute } from './model.js';
8
8
  /** Indexed archive event, including read bookkeeping. */
9
9
  export type ArchiveRecord = Readonly<StoredAct>;
10
10
  export interface Activity {
@@ -14,6 +14,7 @@ export interface Activity {
14
14
  readonly actor: string;
15
15
  readonly body?: string;
16
16
  readonly metadata?: ActivityMetadata;
17
+ readonly delivery?: ActivityDelivery;
17
18
  readonly mentions: readonly string[];
18
19
  readonly reach?: Reach;
19
20
  readonly target?: string;
@@ -49,6 +50,22 @@ export interface ExpressResult {
49
50
  readonly hint?: string;
50
51
  readonly observation?: ExpressObservation;
51
52
  }
53
+ export type ChannelTruncate = number | false;
54
+ export interface ChannelSendInput {
55
+ readonly from: string;
56
+ readonly to: string | readonly string[];
57
+ readonly body: string;
58
+ readonly truncate?: ChannelTruncate;
59
+ readonly metadata?: ActivityMetadata;
60
+ }
61
+ export interface ChannelSendResult {
62
+ readonly id: ActivityId;
63
+ readonly at: number;
64
+ readonly from: string;
65
+ readonly to: readonly string[];
66
+ readonly body: string;
67
+ readonly metadata?: ActivityMetadata;
68
+ }
52
69
  export interface ListenerChangeResult {
53
70
  readonly activity: Activity | null;
54
71
  readonly observation?: ListenerObservation;
@@ -69,6 +86,20 @@ export interface CatchOptions extends ObservationOptions {
69
86
  readonly limit?: number;
70
87
  readonly replace?: boolean;
71
88
  }
89
+ export interface PendingOptions {
90
+ readonly match?: MetadataMatch;
91
+ /** Oldest first; defaults to 100, maximum 100. */
92
+ readonly limit?: number;
93
+ }
94
+ export interface PendingResult {
95
+ readonly activities: readonly PerceivedActivity[];
96
+ /** Matching unconsumed activities outside this page. */
97
+ readonly remaining: number;
98
+ }
99
+ export interface AckResult {
100
+ /** Newly consumed addressed IDs; repeated acknowledgement is a no-op. */
101
+ readonly consumed: number;
102
+ }
72
103
  export type CatchReason = 'drained' | 'idle' | 'capped' | 'quorum' | 'replaced' | 'already-active';
73
104
  export interface CatchResult {
74
105
  readonly activities: readonly PerceivedActivity[];
@@ -128,14 +159,6 @@ export interface HistoryResult {
128
159
  readonly activities: readonly Activity[];
129
160
  readonly observation?: HistoryObservation;
130
161
  }
131
- export interface SquareSnapshot {
132
- readonly context: string;
133
- readonly actCount: number;
134
- readonly hardCap: number | null;
135
- readonly throttlePerMinute?: number;
136
- readonly participants: readonly Pick<ParticipantSummary, 'name' | 'presence' | 'listening'>[];
137
- delivered(name: string, id: ActivityId): boolean;
138
- }
139
162
  export interface OpenOptions {
140
163
  readonly clock?: () => number;
141
164
  readonly wakeTransport?: WakeTransportPort | false;
@@ -170,6 +193,7 @@ export interface JoinResult {
170
193
  }
171
194
  export interface Participant {
172
195
  readonly name: string;
196
+ readonly generation: ActivityId;
173
197
  express(body: string | undefined, options?: ExpressOptions, control?: OperationControl): Promise<ExpressResult>;
174
198
  express(input: ExpressInput, control?: OperationControl): Promise<ExpressResult>;
175
199
  listen(target: string, options?: ObservationOptions, control?: OperationControl): Promise<ListenerChangeResult>;
@@ -177,6 +201,8 @@ export interface Participant {
177
201
  unlisten(target: string, options?: ObservationOptions, control?: OperationControl): Promise<ListenerChangeResult>;
178
202
  listening(options?: ObservationOptions, control?: OperationControl): Promise<ListeningResult>;
179
203
  catch(options?: CatchOptions, control?: OperationControl): Promise<CatchResult>;
204
+ pending(options?: PendingOptions, control?: OperationControl): Promise<PendingResult>;
205
+ ack(ids: readonly ActivityId[], control?: OperationControl): Promise<AckResult>;
180
206
  /** Leave without a message. Only an omitted or empty body is accepted. */
181
207
  done(body?: string, options?: ObservationOptions, control?: OperationControl): Promise<LifecycleResult>;
182
208
  deferDelivery(input: Readonly<{
@@ -1,5 +1,5 @@
1
1
  import { DatabaseSync as MemoryDatabase } from 'node:sqlite';
2
- import { SquareError, nameKey, isWakeRouteKind, validateOwner, validateActivityMetadata, activityMetadataHasContent } from './model.js';
2
+ import { SquareError, nameKey, isWakeRouteKind, validateOwner, validateActivityMetadata, validateActivityDelivery, activityMetadataHasContent } from './model.js';
3
3
  import { validateMetadataMatch, matchingDeferrals, validateDeferralId } from './delivery-deferral.js';
4
4
  import { deriveLanding, landingFacts } from './square-domain.js';
5
5
  import { claimAttempt, prepareAttempt, settleAttempt } from './delivery-attempts.js';
@@ -269,7 +269,16 @@ export function validateStoredActivity(value) {
269
269
  string(a.actor, true);
270
270
  switch (a.kind) {
271
271
  case 'say':
272
- shape(a, [...common], ['body', 'mentions', 'reach', 'reply', 'metadata']);
272
+ shape(a, [...common], ['body', 'mentions', 'reach', 'reply', 'metadata', 'delivery']);
273
+ if (Object.hasOwn(a, 'delivery')) {
274
+ requireFact(a.delivery !== undefined, 'say delivery is malformed.');
275
+ try {
276
+ validateActivityDelivery(a.delivery);
277
+ }
278
+ catch {
279
+ throw invalidArtifact('say delivery is malformed.');
280
+ }
281
+ }
273
282
  if (Object.hasOwn(a, 'body'))
274
283
  string(a.body);
275
284
  if (Object.hasOwn(a, 'metadata')) {
@@ -311,7 +320,7 @@ export function validateStoredActivity(value) {
311
320
  function activity(row) {
312
321
  const h = head(row), payload = object(json(row.payload_json));
313
322
  // Payload cannot shadow indexed structural columns.
314
- const allowed = h.kind === 'say' ? ['body', 'mentions', 'metadata'] : h.kind === 'done' ? ['body'] : h.kind === 'read' ? ['through'] : h.kind === 'listen' || h.kind === 'ignore' || h.kind === 'unlisten' ? ['target'] : [];
323
+ const allowed = h.kind === 'say' ? ['body', 'mentions', 'metadata', 'delivery'] : h.kind === 'done' ? ['body'] : h.kind === 'read' ? ['through'] : h.kind === 'listen' || h.kind === 'ignore' || h.kind === 'unlisten' ? ['target'] : [];
315
324
  requireFact(Object.keys(payload).every(k => allowed.includes(k)));
316
325
  return immutable(validateStoredActivity({ ...h, ...payload }));
317
326
  }
@@ -530,6 +539,27 @@ export function createSqliteDriver(db) {
530
539
  }
531
540
  if (filter.attentionOnly)
532
541
  base.sql += " AND d.route IN ('mention','reply','bell')";
542
+ if (filter.metadataMatch !== undefined) {
543
+ // Walk object keys rather than JSON-path strings: dots, quotes and array-like
544
+ // keys are literal, and arrays must never act as metadata objects.
545
+ base.sql += ` AND NOT EXISTS (
546
+ SELECT 1 FROM json_each(?) field WHERE NOT EXISTS (
547
+ WITH RECURSIVE walk(value,type,depth) AS (
548
+ SELECT json_extract(a.payload_json,'$.metadata'),json_type(a.payload_json,'$.metadata'),0
549
+ UNION ALL
550
+ SELECT child.value,child.type,walk.depth+1
551
+ FROM walk JOIN json_each(CASE WHEN walk.type='object' THEN walk.value ELSE '{}' END) child
552
+ ON child.key=json_extract(field.value,'$.path['||walk.depth||']')
553
+ WHERE walk.depth<json_array_length(field.value,'$.path')
554
+ )
555
+ SELECT 1 FROM walk WHERE depth=json_array_length(field.value,'$.path')
556
+ AND (type=json_type(field.value,'$.equals')
557
+ OR type IN ('integer','real') AND json_type(field.value,'$.equals') IN ('integer','real'))
558
+ AND value IS json_extract(field.value,'$.equals')
559
+ )
560
+ )`;
561
+ base.args.push(encode(filter.metadataMatch.fields));
562
+ }
533
563
  return base;
534
564
  }
535
565
  function directedHead(row, recipient) {
@@ -682,7 +712,6 @@ export function createSqliteDriver(db) {
682
712
  const k = encode(owner(name)), r = one('SELECT MAX(at) AS latest FROM receipts WHERE owner_json=?', [k]), e = one('SELECT MAX(r.act_index) AS latest FROM receipts r JOIN activities a ON a.act_index=r.act_index WHERE r.owner_json=? AND r.act_index>? AND a.actor_json IS NOT NULL', [k, integer(afterIndex, -1)]);
683
713
  return immutable({ ...(r.latest === null ? {} : { latestAt: finite(r.latest) }), ...(e.latest === null ? {} : { latestActorActivityIndex: integer(e.latest) }) });
684
714
  },
685
- receiptAssignments: () => immutable(all('SELECT owner_json,act_index FROM receipts ORDER BY owner_json,act_index').map(r => ({ owner: decoded(r.owner_json, true), index: integer(r.act_index) }))),
686
715
  deliveryDeferrals(name, generation) {
687
716
  const canonical = owner(name);
688
717
  const current = read.seat(canonical);
@@ -13,6 +13,7 @@ interface ToolDefinition {
13
13
  /** Human text identical to what the CLI prints; domain receipts stay inside the adapter. */
14
14
  run(square: Square, args: JsonObject, control: OperationControl, identity: ToolIdentity): Promise<string>;
15
15
  }
16
+ export declare function squareToolsEnabled(env?: NodeJS.ProcessEnv): boolean;
16
17
  export declare const SQUARE_TOOL_INSTRUCTIONS = "Join once. Then express to speak, and catch to take in what reached you. History looks back without taking anything in.";
17
18
  export declare const SQUARE_TOOLS: readonly ToolDefinition[];
18
19
  export declare function squareToolFailure(error: unknown, signal?: AbortSignal): JsonObject;
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod/v4';
2
- import { isSquareError } from './model.js';
2
+ import { isSquareError, SquareError } from './model.js';
3
3
  import { requireOwner, requireJoinName, selectOwnSeat, CATCH_DEFAULT_LIMIT } from './square-api.js';
4
4
  import { renderCatchOutcome, renderExpression, renderFailureLines, renderHistoryResult, renderJoinResult, renderLifecycleResult, renderListenerChange, renderStatusResult, } from './operation-presentation.js';
5
5
  const location = z.string().optional().describe('Which square: a .square file path. Relative paths start from the working directory. Leave it out for the configured square.');
@@ -19,6 +19,14 @@ function tool(name, description, shape, run, render) {
19
19
  return render(receipt, operationArgs) ?? '';
20
20
  } };
21
21
  }
22
+ export function squareToolsEnabled(env = process.env) {
23
+ const raw = env.SQUARE_TOOLS?.trim().toLowerCase();
24
+ if (raw === undefined || raw === '' || raw === '1' || raw === 'true')
25
+ return true;
26
+ if (raw === '0' || raw === 'false' || raw === 'none')
27
+ return false;
28
+ throw new SquareError('invalid_args', 'Invalid SQUARE_TOOLS: expected 0, 1, true, false, or none.');
29
+ }
22
30
  export const SQUARE_TOOL_INSTRUCTIONS = 'Join once. Then express to speak, and catch to take in what reached you. History looks back without taking anything in.';
23
31
  const target = z.string().describe('The participant to turn toward or away from.');
24
32
  export const SQUARE_TOOLS = [
package/dist/views.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { type ActivityId } from './square-core.js';
2
2
  import { type StoredAct } from './model.js';
3
3
  import type { ActivityFilter, SquareRead } from './ports.js';
4
- import type { Activity, ArchiveRecord, PerceivedActivity, HistoryQuery, HistoryContinuation, ParticipantsResult, SquareSnapshot, EntryPresentation, StatusResult, SquareObservation, CatchPresence } from './square-facade.js';
4
+ import type { Activity, ArchiveRecord, PerceivedActivity, HistoryQuery, HistoryContinuation, ParticipantsResult, EntryPresentation, StatusResult, SquareObservation, CatchPresence } from './square-facade.js';
5
5
  export declare const HISTORY_DEFAULT_LIMIT = 10;
6
6
  export declare const HISTORY_MAX_LIMIT = 100;
7
7
  export declare const NON_READ: readonly ["join", "done", "say", "listen", "ignore", "unlisten"];
@@ -17,7 +17,6 @@ export declare function selectHistory(facts: SquareRead, query: HistoryQuery, li
17
17
  totalMatches: number;
18
18
  };
19
19
  export declare function publicActivities(activities: readonly ArchiveRecord[]): Activity[];
20
- export declare function snapshot(facts: SquareRead, now: number): SquareSnapshot;
21
20
  export declare function recoveryObservation(facts: SquareRead, name: string, now: number): {
22
21
  participant: string;
23
22
  activitySummaries: import("./square-facade.js").UnreadActivitySummary[];
package/dist/views.js CHANGED
@@ -19,6 +19,7 @@ export function toPublicActivity(stored) {
19
19
  ...(stored.kind === 'say' && stored.reach !== undefined ? { reach: stored.reach } : {}),
20
20
  ...('body' in stored && stored.body !== undefined ? { body: stored.body } : {}),
21
21
  ...(stored.kind === 'say' && stored.metadata !== undefined ? { metadata: stored.metadata } : {}),
22
+ ...(stored.kind === 'say' && stored.delivery !== undefined ? { delivery: stored.delivery } : {}),
22
23
  mentions: stored.kind === 'say' ? stored.mentions ?? [] : [], ...('target' in stored ? { target: stored.target } : {}),
23
24
  ...(stored.kind === 'say' && stored.reply !== undefined ? { reply: formatActivityId(stored.reply) } : {}) };
24
25
  }
@@ -110,15 +111,6 @@ export function selectHistory(facts, query, limit, kinds, presentation = true) {
110
111
  export function publicActivities(activities) {
111
112
  return activities.flatMap((activity) => activity.kind === 'read' ? [] : [toPublicActivity(activity)]);
112
113
  }
113
- export function snapshot(facts, now) {
114
- const header = facts.header();
115
- const names = new Map(facts.roster().map((p) => [nameKey(p.name), p.name]));
116
- const receipts = new Set(facts.receiptAssignments().map((r) => JSON.stringify([r.owner, r.index])));
117
- return { context: [...header.preamble, ...header.warmup].join('\n'), actCount: facts.activityCount({ kinds: NON_READ }), hardCap: header.hardCap,
118
- ...(header.throttlePerMinute === undefined ? {} : { throttlePerMinute: header.throttlePerMinute }),
119
- participants: coreParticipants(facts, now).filter((p) => p.presence !== 'never-joined').map(({ name, presence, listening }) => ({ name, presence, listening })),
120
- delivered(name, id) { return receipts.has(JSON.stringify([names.get(nameKey(name)) ?? name, parseRequiredActivityId(id)])); } };
121
- }
122
114
  export function recoveryObservation(facts, name, now) {
123
115
  const known = resolveKnownName(facts, name);
124
116
  return { participant: known, activitySummaries: unreadActivitySummaries(facts, known, now), standingCount: inSquareCount(facts),
@@ -8,7 +8,11 @@ Library operations accept a final non-serializable control value:
8
8
 
9
9
  The signal is checked before lifecycle work and before artifact mutation, and it is propagated through artifact reads, transactions, change waits, and ownership lock acquisition. An idle `catch` rejects promptly when cancelled; it does not wake later and consume activity. Cancellation before an artifact transition prevents mutation.
10
10
 
11
- Commit boundaries are one-way. Once an activity or observation commits, later cancellation cannot roll it back or turn the result into a retry. A caller may discard the response after the commit, but the committed activity remains recoverable through `history` or a later `catch`; Square does not add a second consumption acknowledgement phase.
11
+ Commit boundaries are one-way. Once an activity or observation commits, later cancellation cannot
12
+ roll it back or turn the result into a retry. A caller may discard the response after the commit,
13
+ but the committed activity remains recoverable through `history` or a later `catch`; catch does not
14
+ require a second acknowledgement. The separate `pending` query never consumes; its optional
15
+ follow-up `ack(ids)` commits exact receipts with the same cancellation boundary.
12
16
 
13
17
  The single catch lease and independent delivery-deferral leases have distinct lifetimes. Cancelling
14
18
  an active catch releases only that catch's lease. Cancelling a deferral operation before commit
@@ -72,7 +72,7 @@ The amended guidance's reachable-latest clause is applied: the selector remains
72
72
  - `test/core.test.js`: status and participants continue sharing actual authored and receipt times, including a fresh rejoin and watcher.
73
73
  - `test/process/square-cli-render.test.js`: the compact ten-person preview now includes a valid historical never-joined actor in the artifact. Status still hides that actor but its executable continuation uses the full count 14 rather than filtered count 13; participants prints the complete roster. Existing exact rejoin, attention, clipping/public-id continuation and presence feedback remain covered.
74
74
 
75
- No observation timing change is intended: optional viewer discovery still precedes status's resource read; the clock is sampled once inside the transaction, and all domain data, classification, perception and rendering annotations are assembled in that read. Queries perform no I/O or mutations themselves. SDK participant/snapshot shapes and MCP serialization of data remain unchanged. No join/claim is introduced.
75
+ No observation timing change is intended: optional viewer discovery still precedes status's resource read; the clock is sampled once inside the transaction, and all domain data, classification, perception and rendering annotations are assembled in that read. Queries perform no I/O or mutations themselves. At the time, SDK participant/snapshot shapes and MCP serialization of data remained unchanged; the SDK snapshot was subsequently retired. No join/claim is introduced.
76
76
 
77
77
  Production reference search for `coreStatus|StatusObservation|StatusPresentation|ParticipantsPresentation|statusPresentation|participantsPresentation|StatusResult` returns no matches. Neither status nor participants has a duplicate roster in its projection. `git diff --check` passes.
78
78
 
@@ -79,8 +79,10 @@ primitive entrypoints without constructing Square.
79
79
 
80
80
  ## Square composition
81
81
 
82
- Square uses these functions for production landing, catch and `markSeen` against
83
- its existing activity, audience and receipt tables. Numeric activity indexes stay
82
+ Square uses these functions for production landing, catch and `markSeen` (including explicit
83
+ `Participant.ack`) against its existing activity, audience and receipt tables. `Participant.pending`
84
+ uses the same addressed/unconsumed relation without writing receipts; optional metadata matching
85
+ is applied by storage before the page limit. Numeric activity indexes stay
84
86
  internal keys; the primitive does not know Square IDs. Square retains validation,
85
87
  limits, filtering, perception, ownership, leases, hints and error mapping. The
86
88
  empty-catch read probe does not hydrate or acquire a writer; a nonempty probe is
@@ -30,9 +30,9 @@ rg -n 'validateSquareState|exportSqliteState|initializeSqlite|writeSquareSnapsho
30
30
  - `square-state.ts` retains legacy validation for the separately implemented migration and test oracle. Normal v1 recognition validates framing and refuses with `migration_required`; it never decodes v1 history. Common per-activity validation is also used by the row codec.
31
31
  - `artifact.ts::readSquareSnapshot` and `square-sqlite.ts::exportSqliteState` are explicit export/integrity capabilities. Doctor intentionally reads all authority and compares maintained indices against a rebuild. Neither is a hot-runtime fallback.
32
32
  - `createSquareSnapshot` initializes a new artifact. `writeSquareSnapshot/writeSquareFile` are explicit build `--force`/fixture import, not whole-state transaction callbacks. Existing v1 still refuses before destructive work. `initializeSqlite` also supplies the migration's shared rebuild; it calls the same `deriveLanding`, not another production fold.
33
- - Public SDK `snapshot().delivered()` copies raw receipt assignments into a detached exact-owner Set; it does not retain a query or transaction capability and copies no activity history.
33
+ - Historical (retired SDK API): `snapshot().delivered()` copied raw receipt assignments into a detached exact-owner Set; it does not retain a query or transaction capability and copies no activity history.
34
34
  - `.state` matches elsewhere are participant/admission states, transport outcomes or Claude's native `$.state` API, not artifact snapshots. `packages/agent-delivery` remains untouched.
35
- - `test/fixtures/legacy-square-core.js` is a frozen test-only v1 replay oracle. `square_snapshot` in the benchmark is instrumentation for detecting forbidden legacy reads, not a runtime path.
35
+ - `square_snapshot` in the benchmark is instrumentation for detecting forbidden legacy reads, not a runtime path.
36
36
 
37
37
  ### Production source size
38
38
 
@@ -59,7 +59,7 @@ Evidence locations:
59
59
 
60
60
  | Invariant | Tests |
61
61
  |---|---|
62
- | Maintained roster/attention/audience equals independent v1 replay, including gaps, mixed spelling, reply/mention/listen/ignore/bell/done/rejoin priority and order | `test/row-domain.test.js`, frozen `test/fixtures/legacy-square-core.js` |
62
+ | Landing audience priority and directed attention, including mixed spelling, reply/mention/listen/ignore/bell and departure | `test/row-domain.test.js` |
63
63
  | Sparse receipts, exact raw owners/orphans, unseen hole vs later catch, repeat no-op, filtered remaining, historical catch vs latest-join pending, distinct cursor/presence | `test/row-domain.test.js`, `test/perception-projection.test.js` |
64
64
  | Exact v2 schema, optional field preservation, immutable detached facts, expired capabilities, synchronous callbacks, caught storage failure rollback, no-op revision, selected corruption vs doctor | `test/artifact.test.js`, `test/core.test.js` |
65
65
  | Two real processes serialize catches; process death rolls activity and indices back; busy COMMIT retains callback/result; abort preserves reason; revision read has a positive SQL control | `test/sqlite-storage.test.js` |
@@ -1,5 +1,7 @@
1
1
  # Row-oriented artifact design
2
2
 
3
+ > Historical design: the public SDK `Square.snapshot()` and its closure were retired. Snapshot-specific proposals below record the design at the time, not the current API.
4
+
3
5
  **Historical accepted design for artifact format 2.** The current runtime uses format 3 for the
4
6
  `unlisten` event; see [Square API](square-api.md#artifact-format-and-explicit-migration) for current
5
7
  format/migration behavior. The schema and delivery-stage terms below record the original row-storage
@@ -428,7 +430,9 @@ No production structure below carries `.state`, all `.activities`, or a deferred
428
430
  | public Square.snapshot().delivered(name,id) | explicit receiptAssignments + detached roster resolution + small header/status in ONE read txn, as below. Other production callers must not use this for count/standing. |
429
431
  | doctor / explicit import/export tests | full selected-row validation and replay comparison of indices; detached result includes standing count, not state for CLI header. Legacy validator lives only in explicit migration/test oracle. No automatic repair. |
430
432
 
431
- ### Public SDK snapshot closure
433
+ ### Public SDK snapshot closure (retired)
434
+
435
+ This historical design described the removed SDK `Square.snapshot()` API; it is no longer supported.
432
436
 
433
437
  Keep synchronous arbitrary `delivered(name,id)`. `snapshot()` intentionally copies ALL raw receipt assignments into a private detached Set keyed by JSON.stringify([rawOwner,index]), along with an immutable nameKey->first-roster-spelling map. Closure validates/parses id with current parseRequiredActivityId, resolves `map[nameKey(name)] ?? name`, and tests that exact owner/index set. This covers dormant raw owner keys, orphan assigned coordinates, unknown-name fallback, and arbitrary later calls after port close. It does not query a DB or retain SquareRead. Later writes do not alter this snapshot. No receipt times/activity bodies are copied. This O(receipts) exception is necessary for the existing synchronous API, and is limited to explicit SDK snapshot calls; lifecycle/status/poll/render use narrower helpers.
434
438
 
@@ -197,7 +197,7 @@ shared dispatcher's timeout or retry policy.
197
197
 
198
198
  ## One set of operations and defaults
199
199
 
200
- Square owns `history`, `status`, `participants`, `snapshot`, `stream`, and recovery observations.
200
+ Square owns `history`, `status`, `participants`, `stream`, and recovery observations.
201
201
  A file-backed `stream` follows the path with a fresh artifact read for each page, so atomic file
202
202
  replacement is visible even while the resource holds its original connection. Its iterator owns
203
203
  its observation lifetime: `return()` interrupts a pending pull; closing a local Square does not close
@@ -211,7 +211,7 @@ their landed directed activities and adds `perception` and the landing route `vi
211
211
  (`mention`, `reply`, `attention`, or `bell`). Perception follows the viewer's actual landed audience;
212
212
  `via` is absent when no directed route exists. Stream starts at a bounded tail or after an `ActivityId`
213
213
  like `act/12`; it never consumes activity.
214
- Participant owns `express`, `catch`, `listen`, `unlisten`, `ignore`, `listening`, `done`,
214
+ Participant owns `express`, `catch`, `pending`, `ack`, `listen`, `unlisten`, `ignore`, `listening`, `done`,
215
215
  `deferDelivery`, `renewDeliveryDeferral`, and `releaseDeliveryDeferral`. History belongs only to
216
216
  Square; there is no `Participant.history`.
217
217
 
@@ -271,6 +271,62 @@ Operations that accept an `OperationControl` use a final argument with `signal`
271
271
  `onProgress`. Listener/lifecycle operations take optional observation options before that argument;
272
272
  participant selection, seat discovery and host cleanup methods do not take an `OperationControl`.
273
273
 
274
+ ## Pending query and exact acknowledgement
275
+
276
+ `Participant.pending(options?, control?)` and `Participant.ack(ids, control?)` separate a read-only
277
+ query from explicit consumption. `ChannelEndpoint` from `@astrosheep/square/channel` exposes the
278
+ identical methods by forwarding to its captured Participant; it has no independent inbox or storage.
279
+
280
+ `Channel.send` / `Square.send` address joined members, including detached members. Attachment gates
281
+ proactive wake, not landing: a detached recipient retains its unread addressed activity without a
282
+ wake and can query/ack it after reattachment. Recipient validation remains all-or-nothing; a missing
283
+ or departed recipient rejects the entire send without landing activity or waking other recipients.
284
+
285
+ ```ts
286
+ import type { ChannelEndpoint, MetadataMatch } from '@astrosheep/square/channel';
287
+
288
+ async function drain(endpoint: ChannelEndpoint, match: MetadataMatch) {
289
+ for (;;) {
290
+ const page = await endpoint.pending({ match });
291
+ if (page.activities.length === 0) break;
292
+ // Process the entire page successfully before acknowledging its exact IDs.
293
+ for (const activity of page.activities) await processNotice(activity);
294
+ const { consumed } = await endpoint.ack(page.activities.map(activity => activity.id));
295
+ // consumed may be smaller if another consumer acknowledged some IDs first.
296
+ }
297
+ }
298
+ ```
299
+
300
+ - `pending({match?, limit?} = {}, control?)` returns
301
+ `{activities: readonly PerceivedActivity[], remaining: number}`. It selects only landed,
302
+ addressed, unconsumed says, oldest first. Body, metadata, delivery information, perception and
303
+ route use the same public records as catch; bodies are full stored bodies, not clipped previews.
304
+ - Limit defaults to 100 and must be an integer from 1 through 100. The optional `MetadataMatch`
305
+ uses exactly the nested own-property scalar equality semantics described below (AND across fields).
306
+ Storage applies addressedness, consumption and metadata matching **before** limiting.
307
+ `remaining` counts matching unconsumed activities outside the returned page, in the same read
308
+ transaction. It is not the global unread count or a durable cursor.
309
+ - A pending query consumes nothing, changes no read horizon and writes no delivery/presentation
310
+ evidence. Deferrals, catch leases, cancellation and prior clipped presentation do not hide these
311
+ explicit reads. Like catch, pending includes historical addressed receipt holes before a rejoin;
312
+ it is not the proactive delivery eligibility query.
313
+ - `ack(ids: readonly ActivityId[], control?)` returns `{consumed: number}`, the count of newly
314
+ consumed exact IDs. It validates that **every** supplied ID is addressed to this participant before
315
+ recording any receipts. Malformed, nonexistent or unaddressed IDs reject the whole batch with
316
+ `invalid_args` (`reason: 'not_addressed'` for valid but unaddressed IDs). There is no implicit prefix
317
+ acknowledgement, no match reselection and no requirement for an earlier pending call.
318
+ - Duplicate IDs, repeated acknowledgement and an empty array are idempotent. Existing receipt
319
+ timestamps remain unchanged. Acknowledgement uses the same sparse receipts as catch/full
320
+ presentation, so it removes those IDs from pending and ordinary catch, but exact catch remains
321
+ repeatable. It does **not** assert wake acceptance or presentation and writes no such evidence.
322
+ - Both operations require their captured current owner, generation and attached seat; even an empty
323
+ query/ack cannot reuse a detached, retired or replaced handle. No storage format change is needed.
324
+ Cancellation before the receipt transaction prevents consumption; a committed ack is not undone.
325
+ - To drain all matching notices, keep querying and acknowledging pages until an **empty** page.
326
+ Acknowledging one page exposes the next without a cursor. Do not silently stop after the default
327
+ page or treat a snapshot `remaining: 0` as proof that concurrent producers have stopped. Query and
328
+ ack are separate operations, not an exclusive claim or an exactly-once external-effect guarantee.
329
+
274
330
  ## Temporary proactive delivery deferral
275
331
 
276
332
  SDK Participants expose three operations. There are no CLI/MCP/Pi authoring
@@ -307,9 +363,9 @@ try {
307
363
  releasing one cannot end another. A captured current owner/join generation is required for every
308
364
  mutation. Stale handles retain the ownership refusal; done/takeover/rejoin retire old leases.
309
365
  - Lease writes change delivery revision, not conversation history. They do not create a receipt,
310
- change audience or unread counts, hide an archive record, or prohibit explicit catch. Catch alone
311
- consumes normally; a caught record is not replayed when a deferral ends. The catch lease is a
312
- separate, single quiet-wait lease with its existing filters and lifecycle.
366
+ change audience or unread counts, hide an archive record, or prohibit explicit catch/pending/ack.
367
+ Catch and ack consume normally; a consumed record is not replayed when a deferral ends. The catch
368
+ lease is a separate, single quiet-wait lease with its existing filters and lifecycle.
313
369
  - Producer/sweep, atomic claims, session/native projections, final pre-send checks and proactive
314
370
  presentation all share current-generation unexpired matching. Suppressed items get no seen or
315
371
  presented receipt. An active pending observer rechecks on release and the earliest relevant
@@ -339,8 +395,8 @@ or history/status presentation annotations.
339
395
  Express captures optional unread context before the attempt: the latest five directed,
340
396
  unseen activities after the current join, each preview capped at 400 Unicode characters,
341
397
  plus the count outside that preview. It hydrates only that page, not the entire cursor suffix.
342
- This is a nonconsuming preview even when a short body fits in full; only `catch` or the
343
- normal full-notification presentation path consumes it. Count and time facts come
398
+ This is a nonconsuming preview even when a short body fits in full; consumption requires `catch`,
399
+ explicit `ack`, or the normal full-notification presentation path. Count and time facts come
344
400
  from the committed transaction. Listener and lifecycle observations likewise use their mutation's
345
401
  facts, with no postcommit context read. Join uses its mutation's essential facts and treats optional
346
402
  entry/history enrichment as best-effort: `observation.enrichmentUnavailable` is explicit and the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/square",
3
- "version": "0.3.95",
3
+ "version": "0.3.97",
4
4
  "description": "A shared public square where agents join, catch activity, express, and step out when done.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -46,11 +46,14 @@
46
46
  "@types/cross-spawn": "^6.0.6",
47
47
  "@types/node": "^22",
48
48
  "@types/ws": "^8.18.1",
49
- "square-v1": "npm:@astrosheep/square@0.3.70",
50
49
  "typescript": "^5.8",
51
50
  "ws": "^8.21.3"
52
51
  },
53
52
  "exports": {
53
+ "./channel": {
54
+ "types": "./dist/channel.d.ts",
55
+ "default": "./dist/channel.js"
56
+ },
54
57
  "./delivery": {
55
58
  "types": "./dist/delivery-primitives.d.ts",
56
59
  "default": "./dist/delivery-primitives.js"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "square",
3
- "version": "0.3.95",
3
+ "version": "0.3.97",
4
4
  "description": "Native Claude Code inbox delivery for Square participants",
5
5
  "types": "./types.d.ts",
6
6
  "author": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "square",
3
- "version": "0.3.95",
3
+ "version": "0.3.97",
4
4
  "description": "Shared Square activity with reliable participant attention at Codex boundaries.",
5
5
  "author": {
6
6
  "name": "Square"
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "hooks": {
3
3
  "SessionStart": [
4
- { "hooks": [{ "type": "command", "command": "square codex-hook", "timeout": 5 }] }
4
+ { "hooks": [{ "type": "command", "command": "square codex-hook", "timeout": 5, "statusMessage": "Square" }] }
5
5
  ],
6
6
  "SessionResume": [
7
- { "hooks": [{ "type": "command", "command": "square codex-hook", "timeout": 5 }] }
7
+ { "hooks": [{ "type": "command", "command": "square codex-hook", "timeout": 5, "statusMessage": "Square" }] }
8
8
  ],
9
9
  "SessionEnd": [
10
- { "hooks": [{ "type": "command", "command": "square codex-hook", "timeout": 3 }] }
10
+ { "hooks": [{ "type": "command", "command": "square codex-hook", "timeout": 3, "statusMessage": "Square" }] }
11
11
  ],
12
12
  "UserPromptSubmit": [
13
- { "hooks": [{ "type": "command", "command": "square codex-hook", "timeout": 5 }] }
13
+ { "hooks": [{ "type": "command", "command": "square codex-hook", "timeout": 5, "statusMessage": "Square" }] }
14
14
  ],
15
15
  "PostToolUse": [
16
16
  {
@@ -19,7 +19,7 @@
19
19
  "type": "command",
20
20
  "command": "square codex-hook",
21
21
  "timeout": 5,
22
- "statusMessage": "square-codex-hook"
22
+ "statusMessage": "Square"
23
23
  }
24
24
  ]
25
25
  }
@@ -31,7 +31,7 @@
31
31
  "type": "command",
32
32
  "command": "square codex-hook",
33
33
  "timeout": 5,
34
- "statusMessage": "square-codex-hook"
34
+ "statusMessage": "Square"
35
35
  }
36
36
  ]
37
37
  }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Seamless 512px stone tiles, rendered once offline (float canvas noise, dithered, JPEG):
3
+ * veinless honed stone — only the milky drift, on muted grey-white (light) or charcoal (dark).
4
+ * Kept tiny so they inline as data URIs.
5
+ */
6
+ export const MARBLE_LIGHT = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4gHYSUNDX1BST0ZJTEUAAQEAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADb/2wBDAAYEBAUEBAYFBQUGBgYHCQ4JCQgICRINDQoOFRIWFhUSFBQXGiEcFxgfGRQUHScdHyIjJSUlFhwpLCgkKyEkJST/2wBDAQYGBgkICREJCREkGBQYJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCT/wAARCAIAAgADASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECCP/EABYQAQEBAAAAAAAAAAAAAAAAAAABEf/EABYBAQEBAAAAAAAAAAAAAAAAAAABAv/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AOnAGkQUBBcMAAAAAAAAAAAAAAAKAM1K1WaDFRqoAAAIqAioAgKCosBViLAVYiwGoIqCgKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADNjSUGLEarNBEVABFQUAEwxQEUAFRQWNMxYDQigoAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlUBmxmxvExBixMbxLFGMGrExBFMFAFBBUQFRQVUUBUFGtNZ1QXVZ1ZQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExMaEGLEsbsZqjIqAAqCCoCKCiqyqCiaAGiAurKysUblVmVoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAUAAAAAAKxWqlBkUQQFUQaRBnFwFAFQQXBREaRBMWACxqMrFGggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVFqAgACoqAAogpiCKAAAIAogICtRhQblVmVdBQ1NBRnTQaGdNBrRnWpQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASs1qs0EBAXTU01BoRVFixIsBUxQERUARUoIAAAgLKyqjWpqaAogC6gAsWMrqDWrrGroNCSqoAAmqigAAAAAAAAAAAAAAAAAAAAAAAAAAlZrVZoMi1MQRTDFBUIg1GozFBoRVERcAZFQEBQEqpQSggLogg0JpoKiaaCrrOmg0rMUFajKxRQQFUEABQAAAAAAAAAAAAAAAAAAAAAAABKlWogmGLhiiYKgJhikgCioEUFAwARFSggACVUoIjWICCsoLqCAaGAKAosVCINLEigqoqgCgAAAAAAAAAAAAAAAAAAAAAAAIACiCgJi4AIi1AFRYAoAAAAAhRKCAiCgKCKIM1K1UsUZFwxBBcRQAQUAGousxYo0RFgNAAAAAAAAAAAAAAAAAmgommgoaAAAAAAAAAAAgogioqgAAAAABWa0lgM1FwBFBAUwURFqAgUQQxRRkaQAMXEBqRMakAxZDFAAUAAAAAAAAAAAAAKCWs6tZBdXWV0F0TTQWVpjVlBoSVQAAAAAAAARQAAAAAAAABLExpMBBRASqKM1GkoMigIoAJigEBUFkWJGooAAAAAAAAAAAAAAAAFAGbGW8SwGRcAQDACAg1KusxQaEiqAAAAAAAAAAAAAAAAAAACAmKKIlaxmgiNIgiLWaDWjKg0JFiixZQQUIKAAAAAAAAAAAAAAAACVQGUaxMBAxUERUUFRYg1FSKoAAAAAAAAAAAAAAAAAAAAAAJVKDNRazQKgIAYoEaiRYooKAAAAAAAAAAAAAAAAAAAAAYAJhigM4WNIDOCiCxSCgAAAAAAAAAAAAAAAAAAAAAAADNZrdiWAwLggBgosWJFgKCgAAAAAAAAAAAAAAAAAAAAAAAAAAgAKAAAAAAAAAAAAAAAgAKAAAAAAAACYoDOGNGAzhjWAM4uKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAogCgAAAAAAAAAAAAAAAAAAAAAAAAAAAigAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoCgAAAAAAAAAAAAAAACAKAAAgAKAAAAAAACAAoAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAEABQAAAAAAAAAAABNNBQTQURQAAAAAAAAAAAAAAAAAAAAAASgIAAC6gooAAAAAAAAAAAAACKlARagGrqAKqGgoaABqaC6mpoCggACCoCg0gCgAAAAAAAAAAAAAAAAAAAIqUBCpoLpqAKIoKqGoKAoAAAAAAAAAgKIICKKIKgAIC6ampqDWms6aCqixRUUBFBAFFAAAAAAAAAAAAAAAAAAAAAABKqUEqLWUA1NFFXWdXQXTUQG5WtYiwGhFAAAAAAAABAUERUAENAtTS1nQUTRBSVCUGpV1nV1RpWY1AUAAAAAAAAAAAAAAAAAAAAAAAAAEtRNS0FtTU00BAAVFAABVlRYCxpmKCiKAAAAAAACaAzVSgmppWQXUTTQXTUTQa0QQaIzqqNyrKxK1KDcVmNaAAAAAAAAAAAAABQTV1k0GhNNBRNNBRNTQaNZ00GNTU1NBdNQBdE1UFBQCCqCwAVWVgLFIIACgAAamgAiaCpVSgzWa1WagyLiKAigIIDWrGVBqNSsStSg3KusauoN6usSqo1ogChAAAAAAABKqUEqKgBqamg1prOmg1prOmguppUBlNKiC6miKNSrrMWINqzFUVUVBRBRViCDSsxVFEUBLSpQNTUoC6iaaDWomloJUBBEUUQUQZFARTFxRFiYsQaWVlYo3FZlWAqpGgAAAAAAAAGa1WaCVKVEDUEAAA1dZUFShQZqVqxARFAFgsgLFiRqKKAAKIIoqgoqCCoAlVFERpmggAIFRAQAFQAAAAUFRYgYKgCgCrEFGpWozGoCgAAAAAAAVmrUBKzjSAiNJiDI1iAyoqglaSglStVlBBRQVFQWLEjQCgoAALCLAUABFQBAQEVKCIqVRKzatZABcQQXAAAFMIqjOKogAKCoqAIqixqMtAqpFAAAAAABKlVKCAiCoACYoomCmAhVSoFjNaqWAypgoC4uARSRZAFwxYBiY1ggki4CgAAUARFqAVmqiAgAlZaqKIuCgYYKDNhjWGIJigoC4YggCiAANRIsBcWEWARQAAAAAAARQGKjeM4gguGKIsMMAwxcATEaTALExuxmwGcMaxMBFAFixIsQVUVQAAAAAAAAZaTAZFKgzUWoogAAigKigKggoEUUAEqNICCoA1GY1AVUiwFAAAAAAAAAASxQExMaEEwxRRMMUBMTGgBMVKCVFRBBQBYRYooAAAAAAAAAAAJUrTNBEWs0EqLUAVIsQAFDRFBVQ0F1dZ1dQUTQAwVRJGsABYAKAAAAAAAAAAAAAAAAAAAAlVKCVFAAAGokUAAAAAAAAAAAABKpQYrNbrNQYotQBYiqCoAAIBqCi6rIDWmoqCxYkaiigICgoAAAAAAAAAAAAAAAAAAAA/9k=";
7
+ export const MARBLE_DARK = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4gHYSUNDX1BST0ZJTEUAAQEAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADb/2wBDAAYEBAUEBAYFBQUGBgYHCQ4JCQgICRINDQoOFRIWFhUSFBQXGiEcFxgfGRQUHScdHyIjJSUlFhwpLCgkKyEkJST/2wBDAQYGBgkICREJCREkGBQYJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCT/wAARCAIAAgADASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECCP/EABYQAQEBAAAAAAAAAAAAAAAAAAABEf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A54AVFEAUQAAAAAAAAAAAAAAAABVRQWKkAURVBFARQQAAAAQAEABEUBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFiLAVUigKCgAgAgKIKKCIAAJUVAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUAVWV0GtNTTQaE00FEFFQEABQAQQABFARGkBMFKCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAumoA1oigoigAgKIoAACKgAAAKCFioDNFqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKigoCgaIgoigogAAAAAqAKgAgqUEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYJFBQAEVAAAU1AFQAAAFRQAUESqKMi1EAAAXDAQXAEFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACKkagAoCCgIACAAgAKIoCooACgAgYYqKJgogAAAqiIogyKAguIAAAKgAAAAAAAAAAAAAAAAAAAAAAAAAAEaZUGgAAAEUBEVAQVAURQURQAAAAUAABRBRADAAVAQVAQVARQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAUAFEFF0QQUEAABAAAAURQUAAAFEUAFUAEAAAAEFQERUAAARUAAAAAAAAAAAAAAAAAAAAAAAAAAAVAAAFEUAEAAAAAAAVFBQAAAAAVWVBTTUBRFAAARUBBUBBUBAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAVFBRFAABAAUAFAAABRAFQQFSiACAAAAAAAAAAAAAAAAEBQigIoCCmAiNICAAAAAAAAAAAAAAAAAAAAsEUAAAAFEUFEAVABRAAAEqKgAAAAAAAAAAAAAAAAAALFSAKAAqAKgAgqAgAAAAAAAAAAAAAAAAAAAAAAAKIoKIoCooCKAiKAiKAgAAAAAAAAAAAAAAAAACoAqoAogCgAAAlRagAAAAAAAAAAAAAAAAAAAAAAAAKqRQFAAEARUAQAAAAAAAAAAAAAAAAAAAAAAAXRAFNQBoQAqAAAAAAAAAAAAAAAAAAAAAAAAACxUgDQmgACglVEBAAAAAAAAAAAAAAAAAAAAAAAAAAABRFBAAAAAAAAAAAAAAAAAAAAAAAAAAAAU1DQXTUAXRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVFBBQEFAQAAAAAAAAAAAAAAAFAAABBUAAABQRQAAAAAAARUAAAAAAAAAAAAAAAAAAAAABQAUEFAQVAQVAAAAAAAAAAAAAAAUAAAAAAxTATBQEFQAAAEBRAAAAAAAAAAAAAAAAAAAAAAABUUBUUAUBBQEwxQGcRpAQAAAAAAAAABUAURQBQAFAAABVGRUQRGqgIAAAAAAAAAAAAAAAAAAAAAAAAAChi4AKAAqiAIAAIACIoCCoAAAAAAACgAsAXBYCKKCC4AgoCIoCM1pFGaLYiAAAAAAAAAAAAAACooCCgIKmACgILhgNYYoACggACLUABAAAQAEAAAAAAFAAAFiKCxYiwFCAAAAoohioCJWkBmo0iCIoCCgIAAAAAAAACgAoIooJiY0YDKrgCLgqigoIKAiNIDIqICKAgUARQERQEAAUAFAAUBBQBQAVBRQAFQBUABFQBFQERRBEVAAAAAAAAAFiKCgoAKAAAACKALFZVRQAEBBEVKCAKACACAIqAAoAAKqAKAACgAoIKigoIAgoAIAAAIoJiiCVlpKCAAAAAAAAKigsVAFABRFABFFEEFisqCiCioCCBUAAAAAQKCAAKigAAKigoAKEVQBAURQEAAAAQQFQBUAAAERQERUAAAAAABRFBQAAAFQBRNUARQFRQAABACoACCAuiAKgAAAAAoACgAoAoigCCiiAKIIAAAgCgAAAJVQBABKAAAAAAAAqAKqQBQAAQFEAURQFZAaEAUQARagCAAAAAAAAAAqKAqAKqKACqIoAgCAigIAACAqoAoigIqAgIAAAAAAAAAAAACiAKgAaaALpqACoAqooAACCUAAAAAAAAAAAABUUFVFAUAEVAAFAUQRFAQVAAAAAEABFQAAAAAAAAAAAAAAAAAAAAAAFAAAARUAAAAAAAAAAAAAABqKysBoRQAFEAQAVRBQEMUQQAERUAQAAAAAAAAAAAAAAAAAAAAAAAf/9k=";