@astrosheep/square 0.3.105 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/activity-feed.js +1 -1
- package/dist/artifact-store.d.ts +41 -0
- package/dist/artifact-store.js +272 -0
- package/dist/artifact.d.ts +8 -12
- package/dist/artifact.js +66 -343
- package/dist/attention-presentation.d.ts +33 -7
- package/dist/attention-presentation.js +33 -69
- package/dist/boundary-presentation.d.ts +0 -2
- package/dist/boundary-presentation.js +17 -22
- package/dist/catch-decisions.d.ts +2 -2
- package/dist/catch-decisions.js +14 -4
- package/dist/catch-selection.d.ts +8 -0
- package/dist/catch-selection.js +5 -0
- package/dist/channel-artifact.d.ts +10 -0
- package/dist/channel-artifact.js +55 -0
- package/dist/channel-delivery.d.ts +11 -0
- package/dist/channel-delivery.js +51 -0
- package/dist/channel-domain.d.ts +3 -0
- package/dist/channel-domain.js +83 -0
- package/dist/channel-facade.d.ts +76 -0
- package/dist/channel-facade.js +1 -0
- package/dist/channel-internal.d.ts +126 -0
- package/dist/channel-internal.js +440 -0
- package/dist/channel-metadata.d.ts +11 -0
- package/dist/channel-metadata.js +1 -0
- package/dist/channel-ports.d.ts +241 -0
- package/dist/channel-ports.js +1 -0
- package/dist/channel-presentation.d.ts +17 -0
- package/dist/channel-presentation.js +22 -0
- package/dist/channel-sqlite-schema.d.ts +13 -0
- package/dist/channel-sqlite-schema.js +81 -0
- package/dist/channel-transport.d.ts +62 -0
- package/dist/channel-transport.js +1 -0
- package/dist/channel-values.d.ts +27 -0
- package/dist/channel-values.js +90 -0
- package/dist/channel.d.ts +31 -21
- package/dist/channel.js +42 -74
- package/dist/claude-delivery-status.js +1 -1
- package/dist/claude-delivery.d.ts +2 -5
- package/dist/claude-delivery.js +56 -103
- package/dist/claude-mod.js +4 -4
- package/dist/claude-transport.d.ts +5 -0
- package/dist/claude-transport.js +58 -0
- package/dist/cli/context.d.ts +2 -2
- package/dist/cli/context.js +0 -1
- package/dist/cli/maintenance-commands.d.ts +0 -4
- package/dist/cli/maintenance-commands.js +2 -29
- package/dist/cli/program.js +1 -1
- package/dist/cli/registry.js +1 -2
- package/dist/codex-address.d.ts +1 -1
- package/dist/codex-queue.d.ts +2 -2
- package/dist/codex-queue.js +1 -3
- package/dist/decisions.js +3 -3
- package/dist/delivery-deferral.d.ts +9 -18
- package/dist/delivery-deferral.js +4 -4
- package/dist/delivery-health.d.ts +4 -4
- package/dist/delivery-health.js +11 -11
- package/dist/delivery-operations.d.ts +15 -2
- package/dist/delivery-operations.js +60 -30
- package/dist/delivery.d.ts +44 -33
- package/dist/delivery.js +49 -75
- package/dist/help.js +1 -6
- package/dist/http-delivery.d.ts +2 -2
- package/dist/http-delivery.js +2 -4
- package/dist/inbox.d.ts +10 -18
- package/dist/inbox.js +44 -53
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.js +40 -70
- package/dist/message.d.ts +7 -0
- package/dist/message.js +23 -0
- package/dist/model.d.ts +41 -77
- package/dist/model.js +7 -139
- package/dist/neutral.d.ts +72 -0
- package/dist/neutral.js +27 -0
- package/dist/notifications.d.ts +2 -8
- package/dist/notifications.js +15 -50
- package/dist/operation-presentation.d.ts +1 -2
- package/dist/operation-presentation.js +1 -7
- package/dist/owner.d.ts +2 -2
- package/dist/owner.js +1 -1
- package/dist/paseo-delivery.d.ts +1 -1
- package/dist/paseo-delivery.js +2 -4
- package/dist/paseo-timeline.js +1 -0
- package/dist/plugins/opencode/delivery.js +27 -42
- package/dist/ports.d.ts +46 -168
- package/dist/presentation-operations.d.ts +1 -14
- package/dist/presentation-operations.js +16 -16
- package/dist/presentation.d.ts +5 -5
- package/dist/presentation.js +1 -0
- package/dist/runtime.js +2 -2
- package/dist/search.js +1 -1
- package/dist/sqlite-channel.d.ts +15 -0
- package/dist/sqlite-channel.js +600 -0
- package/dist/sqlite-framing.d.ts +8 -0
- package/dist/sqlite-framing.js +31 -0
- package/dist/sqlite-square.d.ts +12 -13
- package/dist/sqlite-square.js +192 -248
- package/dist/sqlite-values.d.ts +21 -0
- package/dist/sqlite-values.js +78 -0
- package/dist/square-api.d.ts +1 -1
- package/dist/square-api.js +1 -1
- package/dist/square-core.d.ts +1 -1
- package/dist/square-domain.d.ts +21 -7
- package/dist/square-domain.js +93 -94
- package/dist/square-facade.d.ts +12 -66
- package/dist/square-index.d.ts +1 -1
- package/dist/square-index.js +1 -1
- package/dist/square-location.d.ts +6 -1
- package/dist/square-location.js +1 -1
- package/dist/square-receive.d.ts +2 -3
- package/dist/square-sqlite.d.ts +45 -44
- package/dist/square-sqlite.js +308 -764
- package/dist/square-tools.d.ts +8 -0
- package/dist/square-tools.js +39 -0
- package/dist/views.d.ts +6 -6
- package/dist/views.js +15 -9
- package/dist/wake-preview.d.ts +7 -0
- package/dist/wake-preview.js +15 -0
- package/dist/wake-sink.d.ts +2 -2
- package/dist/wake-sink.js +1 -1
- package/dist/wake-transports.d.ts +7 -0
- package/dist/wake-transports.js +42 -0
- package/guides/claude-native.md +13 -10
- package/guides/codex-delivery.md +3 -1
- package/guides/participant-ownership.md +1 -1
- package/guides/row-runtime-delivery-evidence.md +2 -0
- package/guides/row-storage-design.md +19 -44
- package/guides/square-api.md +137 -60
- package/package.json +3 -3
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/types.d.ts +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/opencode/delivery.ts +28 -41
- package/plugins/pi/square-pi.js +28 -51
- package/dist/square-legacy-framing.d.ts +0 -11
- package/dist/square-legacy-framing.js +0 -64
- package/dist/square-migration-host-ledger.d.ts +0 -26
- package/dist/square-migration-host-ledger.js +0 -169
- package/dist/square-migration.d.ts +0 -30
- package/dist/square-migration.js +0 -599
- package/guides/migration.md +0 -156
package/guides/square-api.md
CHANGED
|
@@ -224,7 +224,8 @@ Square; there is no `Participant.history`.
|
|
|
224
224
|
body may carry `{}`; an omitted, empty or whitespace-only body requires non-empty metadata. Metadata-only
|
|
225
225
|
says omit `body` from public activity records. Invalid objects, cycles, unsupported values and oversized
|
|
226
226
|
metadata refuse before landing with `activityUnsent`. Metadata is carried unchanged by express, history,
|
|
227
|
-
catch, status/latest, unread projections and stream records through the SDK, CLI and stream
|
|
227
|
+
catch, status/latest, unread projections and stream records through the SDK, CLI and stream, and
|
|
228
|
+
by channel send/receive/history/pending messages.
|
|
228
229
|
MCP and Pi tools are not a metadata surface: their `express` requires a body and its schema rejects a
|
|
229
230
|
`metadata` argument. Tool history/catch return rendered text with a structured-activity placeholder,
|
|
230
231
|
not metadata JSON or activity objects. Metadata does not affect mentions, replies, listening, audience,
|
|
@@ -232,14 +233,15 @@ Square; there is no `Participant.history`.
|
|
|
232
233
|
by exact metadata match through the SDK lease API below. Metadata may nest up to 256 levels; the bound is enforced before
|
|
233
234
|
persistence and public reads. Human text uses a concise structured-activity placeholder and never prints
|
|
234
235
|
metadata JSON. SDK history and pending queries accept exact metadata matches; there is no query
|
|
235
|
-
language or metadata index. CLI `--metadata <json>` remains an express input flag.
|
|
236
|
+
language or metadata index. CLI `--metadata <json>` remains an express input flag. Channel
|
|
237
|
+
`send` accepts the same metadata and returns it on `ChannelMessage` records.
|
|
236
238
|
- Catch returns `{activities, remaining, reason, hint?, observation?}`. Each activity includes
|
|
237
239
|
viewer-relative `perception` and `via`. Reasons are `drained`, `idle`, `capped`, `quorum`,
|
|
238
240
|
`replaced`, or `already-active`. Omitted/zero idle is immediate; positive idle uses the single
|
|
239
241
|
leased quiet wait. Receipt selection, perception, routes, counts and terminal facts settle
|
|
240
242
|
in the receipt transaction. Cancellation/replacement releases only that waiter's lease and never
|
|
241
243
|
consumes future activity.
|
|
242
|
-
-
|
|
244
|
+
- Agora history (`Square.history`) returns `{activities}`. Default page size is ten;
|
|
243
245
|
`limit: null` explicitly requests an unbounded archive. Numeric limits must be 1–100. Contextual
|
|
244
246
|
queries still reject an implicit merged window over 100 activities; an explicit numeric limit
|
|
245
247
|
bounds it. CLI/tool schemas accept numeric limits only. History never writes receipts; history
|
|
@@ -271,9 +273,68 @@ Operations that accept an `OperationControl` use a final argument with `signal`
|
|
|
271
273
|
`onProgress`. Listener/lifecycle operations take optional observation options before that argument;
|
|
272
274
|
participant selection, seat discovery and host cleanup methods do not take an `OperationControl`.
|
|
273
275
|
|
|
276
|
+
## Channel endpoints and point-to-point messages
|
|
277
|
+
|
|
278
|
+
`@astrosheep/square/channel` exports the endpoint and messaging resource `Channel`; the Agora
|
|
279
|
+
resource (`Square` / `Participant`) uses the same message substrate. `Channel` has static `open`,
|
|
280
|
+
`create` and `inMemory`, a `location` property, and methods `attach`, `reattach`, `endpoint`,
|
|
281
|
+
`endpointsOf`, `listEndpoints`, `detach`, `cancelDeliveries`, `remove`, `send`, `history` and
|
|
282
|
+
`close`. Obtain a `ChannelEndpoint` from a Channel; it has `name` and `generation` properties and
|
|
283
|
+
methods `pending`, `ack`, `receive`, `deferDelivery`, `renewDeliveryDeferral`,
|
|
284
|
+
`releaseDeliveryDeferral`, `send` and `leave`. Endpoint generations are opaque strings: retain
|
|
285
|
+
and compare them for equality, without interpreting their contents.
|
|
286
|
+
|
|
287
|
+
The subpath exports the types `MessageId`, `MessageCursor`, `MessageQuery`, `MessageSearch`,
|
|
288
|
+
`MessageMetadata`, `ChannelTruncate`, `ChannelSendInput`, `ChannelSendResult`, `ChannelMessage`,
|
|
289
|
+
`ChannelPendingOptions`, `ChannelPendingResult`, `ChannelReceiveResult`, `ChannelHistoryQuery`,
|
|
290
|
+
`ChannelHistoryResult`, `AckResult`, `ChannelEndpointSummary`, `ChannelEndpointHold`,
|
|
291
|
+
`DeliveryDeferral`, `MetadataMatch`, `Owner`, `OperationControl`, `WakeAddress`, `WakeTransportPort`,
|
|
292
|
+
`ChannelOpenInput`, `ChannelCreateInput`, `HarnessSessionChannel` and `ResolvedHarnessSession`.
|
|
293
|
+
It also exports `harnessOwner`, `decodeHarnessOwner`, `localUserOwner` and `resolveHarnessSession`.
|
|
294
|
+
The package root exports `MessageId`, `MessageCursor` and `MessageQuery`, but not `MessageMetadata`
|
|
295
|
+
or `MessageSearch` under those names; its Agora equivalents are `ActivityMetadata` and `ActivitySearch`.
|
|
296
|
+
|
|
297
|
+
Square and Participant expose their own Agora lifecycle operations; in particular,
|
|
298
|
+
`square.endHeld` lands a fenced `done`, whereas `channel.remove` removes an endpoint and is not
|
|
299
|
+
`Channel.endHeld`.
|
|
300
|
+
`Square.history` and `Participant.pending` remain Agora projections, not channel reads: Agora
|
|
301
|
+
history selects joins, says, departures and listener changes but never native messages, and Agora
|
|
302
|
+
pending selects addressed, unconsumed says as perceived activities.
|
|
303
|
+
|
|
304
|
+
- `Channel.send({from, to, body, truncate?, metadata?})` addresses standing endpoints explicitly.
|
|
305
|
+
`truncate` defaults to `false`, so the full body is delivered and presentation records a receipt.
|
|
306
|
+
An explicit positive integer at least as long as the body is likewise unclipped; when the body is
|
|
307
|
+
longer, the landed message stores `preview`, so presentation shows that preview and records clipped
|
|
308
|
+
coverage without consuming the full stored body. `pending`, `receive` and `ack` still read and
|
|
309
|
+
consume that full body. Any other `truncate` value is refused before landing. `to` in the send
|
|
310
|
+
result and in every `ChannelMessage` is the actual landed audience after address resolution. A
|
|
311
|
+
stored message may have an empty body only when imported from a legacy metadata-only direct say;
|
|
312
|
+
`Channel.send` itself always requires a non-empty body.
|
|
313
|
+
- `endpoint.receive(options?, control?)` selects the oldest unconsumed addressed messages matching the
|
|
314
|
+
shared filters and records their receipts in one transaction, returning
|
|
315
|
+
`{messages, consumed, remaining}`. Each `ChannelMessage` is `{id, at, from, to, body, metadata?}`,
|
|
316
|
+
symmetric with the send result. `remaining` counts matching unconsumed messages left after that
|
|
317
|
+
transaction. Nothing is consumed by `pending` or `history`.
|
|
318
|
+
- `Channel.history(query?, control?)` returns `{messages}` for native messages and says, selecting
|
|
319
|
+
the newest page oldest-first by default; `order: 'desc'` reverses the page. It shares the
|
|
320
|
+
`MessageQuery` filters (`at`, `from`, `match`, `search`, `cursor`) but has no Agora
|
|
321
|
+
`contextBefore`/`contextAfter`, `since`, `mention` or `observe` options. Its default page size is
|
|
322
|
+
100; `limit` accepts 1–100 or `null` for all matches.
|
|
323
|
+
- `listEndpoints(control?)` reports every standing endpoint without an owner argument as
|
|
324
|
+
`{name, generation, attachment, deliveryAvailable, unreadCount, lastActiveAt?}`. `endpointsOf(owner)`
|
|
325
|
+
remains the owner-scoped custody list, and neither operation joins, restores or reveals an address.
|
|
326
|
+
- Direct notifications have no separate envelope: the model-facing text is the stored preview, or
|
|
327
|
+
the body when untruncated, escaped inside the shared `<system-reminder>` frame; sender, recipients
|
|
328
|
+
and ID are not printed. Body text that would read as `<channel-`, `<square-` or
|
|
329
|
+
`<system-reminder` is escaped. A mixed boundary keeps notification order; its fixed budget and
|
|
330
|
+
`N more activities not shown.` line apply only to Agora activity blocks, and the line trails the
|
|
331
|
+
rendered batch.
|
|
332
|
+
- An unconsumed direct message stays eligible for the proactive inbox and wake horizon across a seat
|
|
333
|
+
takeover or rejoin. Earlier Agora activity still ends at the recipient's new read horizon.
|
|
334
|
+
|
|
274
335
|
## Shared history and pending filters
|
|
275
336
|
|
|
276
|
-
`HistoryQuery` and `PendingOptions` extend the
|
|
337
|
+
Agora `HistoryQuery` and `PendingOptions` extend the exported `ActivityQuery`:
|
|
277
338
|
|
|
278
339
|
```ts
|
|
279
340
|
interface ActivityQuery {
|
|
@@ -285,6 +346,12 @@ interface ActivityQuery {
|
|
|
285
346
|
}
|
|
286
347
|
```
|
|
287
348
|
|
|
349
|
+
Channel `ChannelHistoryQuery` and `ChannelPendingOptions` extend `MessageQuery`, whose `at` IDs
|
|
350
|
+
and `MessageCursor.id` are `MessageId` (`msg/N`) rather than `ActivityId` (`act/N`). The message
|
|
351
|
+
query types are exported from `@astrosheep/square/channel` and the package root. Channel history,
|
|
352
|
+
pending, receive and ack use `msg/N`; Agora history, pending and ack use `act/N`. A say has both
|
|
353
|
+
coordinates, which are not interchangeable.
|
|
354
|
+
|
|
288
355
|
- Filters combine with AND. IDs in `at` and senders in `from` each combine with OR; an absent array
|
|
289
356
|
is unrestricted, while an explicitly empty array matches nothing. `at` is an exact ID selection;
|
|
290
357
|
only history can expand those IDs with `contextBefore` / `contextAfter`.
|
|
@@ -294,13 +361,15 @@ interface ActivityQuery {
|
|
|
294
361
|
- `match` uses the same nested own-property, exact type-sensitive scalar equalities as deferrals.
|
|
295
362
|
`search` tests each activity's ID, sender and body independently, case-insensitively. Literal mode
|
|
296
363
|
treats regex characters as plain text; regex mode uses JavaScript's built-in regular expressions,
|
|
297
|
-
without a ReDoS timeout. Search text must be nonempty and regex syntax must be valid.
|
|
298
|
-
searches says and departures, not joins
|
|
299
|
-
|
|
364
|
+
without a ReDoS timeout. Search text must be nonempty and regex syntax must be valid. Agora
|
|
365
|
+
history searches says and departures, not joins. Participant `pending` selects addressed,
|
|
366
|
+
unconsumed says; `ChannelEndpoint` `pending`/`receive` select addressed, unconsumed messages and
|
|
367
|
+
says.
|
|
368
|
+
- A cursor is an exclusive boundary in its own coordinate space, independent of `limit`: before excludes the anchor
|
|
300
369
|
and everything newer; after excludes the anchor and everything older. No consumption is involved.
|
|
301
370
|
Storage applies the shared filters before limiting and uses the same predicates for counts.
|
|
302
|
-
-
|
|
303
|
-
selects the newest page and returns it oldest first; an after cursor selects the oldest following
|
|
371
|
+
- Agora history keeps its archive-specific `order`, `mention`, `since` and context options. By
|
|
372
|
+
default it selects the newest page and returns it oldest first; an after cursor selects the oldest following
|
|
304
373
|
page. `order: 'desc'` reverses the selected page, not which activities belong to it. Numeric limits
|
|
305
374
|
stay 1–100, default ten; `limit: null` reads all matches. Exact/context history keeps its existing
|
|
306
375
|
implicit full-window selection with a 100-activity bound unless a numeric limit is supplied.
|
|
@@ -311,18 +380,24 @@ interface ActivityQuery {
|
|
|
311
380
|
The SDK accepts only `search` and `cursor`, not the removed `grep` / `fixed` / `before` / `after`
|
|
312
381
|
fields. CLI flags and tool arguments keep those familiar spellings and translate at the adapter
|
|
313
382
|
boundary, e.g. `--fixed text` becomes `search: {text, mode: 'literal'}` and `--after act/12` becomes
|
|
314
|
-
`cursor: {id: 'act/12', direction: 'after'}`. The
|
|
383
|
+
`cursor: {id: 'act/12', direction: 'after'}`. The query surface keeps these `act/N` and `msg/N` coordinates; format 11 is a separate fresh-store
|
|
384
|
+
breaking upgrade rather than a query change.
|
|
315
385
|
|
|
316
386
|
## Pending query and exact acknowledgement
|
|
317
387
|
|
|
318
388
|
`Participant.pending(options?, control?)` and `Participant.ack(ids, control?)` separate a read-only
|
|
319
|
-
query from explicit consumption. `
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
389
|
+
query from explicit consumption. Agora `pending` returns addressed, unconsumed `say` records as
|
|
390
|
+
perceived activities, so native messages never appear there. `ChannelEndpoint` from
|
|
391
|
+
`@astrosheep/square/channel` returns `{messages}` for both native messages and says; `ack` records
|
|
392
|
+
receipts for the addressed message IDs. Neither layer keeps a second inbox, read horizon or storage.
|
|
393
|
+
|
|
394
|
+
Agora publication is participant-scoped: `Participant.express(...)` publishes a say/activity for
|
|
395
|
+
that standing participant. Standalone `Channel.send` addresses standing Channel endpoints, without
|
|
396
|
+
requiring Agora membership, and lands a Channel message, not an Agora activity. Attachment gates
|
|
397
|
+
proactive wake, not landing: a detached Channel recipient retains its unread addressed item and can
|
|
398
|
+
query/ack it after reattachment. Recipient validation is all-or-nothing; a missing or departed
|
|
399
|
+
recipient rejects the entire send without landing a message or waking other recipients. An omitted
|
|
400
|
+
`truncate` sends the full body; truncation is an explicit per-message choice.
|
|
326
401
|
|
|
327
402
|
```ts
|
|
328
403
|
import type { ChannelEndpoint, MetadataMatch } from '@astrosheep/square/channel';
|
|
@@ -330,30 +405,36 @@ import type { ChannelEndpoint, MetadataMatch } from '@astrosheep/square/channel'
|
|
|
330
405
|
async function drain(endpoint: ChannelEndpoint, match: MetadataMatch) {
|
|
331
406
|
for (;;) {
|
|
332
407
|
const page = await endpoint.pending({ match });
|
|
333
|
-
if (page.
|
|
408
|
+
if (page.messages.length === 0) break;
|
|
334
409
|
// Process the entire page successfully before acknowledging its exact IDs.
|
|
335
|
-
for (const
|
|
336
|
-
const { consumed } = await endpoint.ack(page.
|
|
410
|
+
for (const message of page.messages) await processNotice(message);
|
|
411
|
+
const { consumed } = await endpoint.ack(page.messages.map(message => message.id));
|
|
337
412
|
// consumed may be smaller if another consumer acknowledged some IDs first.
|
|
338
413
|
}
|
|
339
414
|
}
|
|
340
415
|
```
|
|
341
416
|
|
|
342
|
-
- `pending(query: PendingOptions = {}, control?)` returns
|
|
343
|
-
`{activities: readonly PerceivedActivity[], remaining: number}
|
|
344
|
-
|
|
345
|
-
|
|
417
|
+
- `Participant.pending(query: PendingOptions = {}, control?)` returns
|
|
418
|
+
`{activities: readonly PerceivedActivity[], remaining: number}` and selects landed, addressed,
|
|
419
|
+
unconsumed says oldest first, with viewer-relative `perception` and `via` as in catch.
|
|
420
|
+
`ChannelEndpoint.pending` returns `{messages: readonly ChannelMessage[], remaining: number}` for
|
|
421
|
+
addressed, unconsumed messages and says; each message is `{id, at, from, to, body, metadata?}`.
|
|
422
|
+
Both read full stored bodies, never clipped previews or delivery remnants.
|
|
346
423
|
- Limit defaults to 100 and must be an integer from 1 through 100. The shared filters above are
|
|
347
424
|
available alongside that limit. Storage applies addressedness, consumption and all supplied
|
|
348
|
-
filters **before** limiting. `remaining` counts matching unconsumed activities
|
|
349
|
-
page in the same read transaction. It is not the global
|
|
425
|
+
filters **before** limiting. `remaining` counts matching unconsumed activities for Agora and
|
|
426
|
+
messages for Channel outside the returned page in the same read transaction. It is not the global
|
|
427
|
+
unread count or a durable cursor.
|
|
350
428
|
- A pending query consumes nothing, changes no read horizon and writes no delivery/presentation
|
|
351
429
|
evidence. Deferrals, catch leases, cancellation and prior clipped presentation do not hide these
|
|
352
|
-
explicit reads.
|
|
353
|
-
|
|
354
|
-
|
|
430
|
+
explicit reads. Agora catch and pending include historical addressed activity receipt holes from
|
|
431
|
+
before a rejoin; they are not proactive delivery eligibility queries. Channel pending, receive and
|
|
432
|
+
ack use only addresses valid for the current endpoint generation. Ordinary unfenced `Channel.send`
|
|
433
|
+
messages remain addressed across takeover or rejoin until consumed.
|
|
434
|
+
- Agora `ack(ids: readonly ActivityId[], control?)` and Channel `ack(ids: readonly MessageId[], control?)`
|
|
435
|
+
return `{consumed: number}`, the count of newly
|
|
355
436
|
consumed exact IDs. It validates that **every** supplied ID is addressed to this participant before
|
|
356
|
-
recording any receipts. Malformed, nonexistent or unaddressed IDs reject the whole batch with
|
|
437
|
+
recording any receipts; Channel also checks that the address is valid for the current generation. Malformed, nonexistent or unaddressed IDs reject the whole batch with
|
|
357
438
|
`invalid_args` (`reason: 'not_addressed'` for valid but unaddressed IDs). There is no implicit prefix
|
|
358
439
|
acknowledgement, no match reselection and no requirement for an earlier pending call.
|
|
359
440
|
- Duplicate IDs, repeated acknowledgement and an empty array are idempotent. Existing receipt
|
|
@@ -365,7 +446,8 @@ async function drain(endpoint: ChannelEndpoint, match: MetadataMatch) {
|
|
|
365
446
|
Cancellation before the receipt transaction prevents consumption; a committed ack is not undone.
|
|
366
447
|
- To drain all matching notices, keep querying and acknowledging pages until an **empty** page.
|
|
367
448
|
Acknowledging one page exposes the next without a cursor. For read-only pagination instead, pass
|
|
368
|
-
`cursor: {id: page.
|
|
449
|
+
`cursor: {id: page.messages.at(-1)!.id, direction: 'after'}` on a channel endpoint (or
|
|
450
|
+
`page.activities` on a Participant) with the same filters; earlier pages
|
|
369
451
|
stay unconsumed. Do not silently stop after the default page or treat a snapshot `remaining: 0`
|
|
370
452
|
as proof that concurrent producers have stopped. Query and
|
|
371
453
|
ack are separate operations, not an exclusive claim or an exactly-once external-effect guarantee.
|
|
@@ -480,37 +562,32 @@ It overlaps at most four receiving sessions per dispatch, preserving order withi
|
|
|
480
562
|
session and draining started work even on failure. Claims, final freshness checks and
|
|
481
563
|
uncertain-delivery suppression remain unchanged. Paseo inventory discovery is asynchronous;
|
|
482
564
|
overlapping attempts share only the in-flight inventory read, never a cached idle status.
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
`discover(input, options?, control?)`, `inbox(input, owner, options?, control?)`,
|
|
486
|
-
`diagnoseSquare(input, control?)
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
Historic host-cleanup departures cannot be distinguished from voluntary `done` and stay done.
|
|
503
|
-
For formats 2/3, also freeze and back up the legacy HostLedger used for verified owner/evidence
|
|
504
|
-
import; no authority is manufactured for done participants. Replay-critical native ambiguity
|
|
505
|
-
refuses rather than disappears, including any nonempty legacy token-only tombstones.
|
|
506
|
-
Current-format migration is a byte-preserving no-op. See [migration.md](migration.md) for the
|
|
507
|
-
narrow metadata policy, source-preserving refusal and filesystem requirements. Updating a package
|
|
508
|
-
does not migrate files or install a new runtime.
|
|
565
|
+
Paseo inventory discovery returns a promise.
|
|
566
|
+
|
|
567
|
+
`discover(input, options?, control?)`, `inbox(input, owner, options?, control?)`, and
|
|
568
|
+
`diagnoseSquare(input, control?)` are ordinary public exports. They are not methods on a synthetic
|
|
569
|
+
cross-square object. Diagnose resolves a path without opening a valid current-format Square first.
|
|
570
|
+
|
|
571
|
+
## Artifact format
|
|
572
|
+
|
|
573
|
+
The current artifact format is 11. It is a breaking fresh-store format, not an upgrade of format
|
|
574
|
+
10. A full Square file combines three separately owned layers in one SQLite database and one
|
|
575
|
+
transaction: the neutral storage revision header, the same Channel message/endpoint/delivery
|
|
576
|
+
tables used by a standalone Channel, and the Agora social tables plus the activity allocator and
|
|
577
|
+
square settings. Standalone Channel storage uses its own format identity and version and contains
|
|
578
|
+
only the neutral and Channel structures; it never creates or reads Agora tables.
|
|
579
|
+
|
|
580
|
+
Ordinary opens accept only the exact current identity, version and owned schema. Any other
|
|
581
|
+
identity or version is refused read-only, with no conversion, no write and no migrate command.
|
|
582
|
+
There is no automatic upgrade, compatibility decoder or recovery instruction. Stop any session
|
|
583
|
+
using an older `.square` file and start a fresh square; the old bytes are never touched.
|
|
509
584
|
|
|
510
585
|
## SDK breaking migration: opaque owners
|
|
511
586
|
|
|
512
|
-
Artifact format 7 introduced opaque owners
|
|
513
|
-
|
|
587
|
+
Artifact format 7 introduced opaque owners, format 8 removed hold/resume activities, and format 9
|
|
588
|
+
stores native messages. Format 10 unified message content while keeping Agora relations separate.
|
|
589
|
+
Format 11 splits neutral storage, standalone Channel and Agora, and has no migration from any
|
|
590
|
+
older format. Current artifacts must be created fresh at format 11.
|
|
514
591
|
`Body`, `sameBody` and `validateBody` are removed from the core and public API. Replace identity
|
|
515
592
|
objects with an explicit owner at each identity-bearing operation; do not put identity on a resource:
|
|
516
593
|
|
|
@@ -551,5 +628,5 @@ are deleted. Consumers must open/close an actual Square and select a Participant
|
|
|
551
628
|
full archive reads with `limit: null`. SDK express now waits by default. MCP constructor configuration
|
|
552
629
|
uses `path`, consistent with `Square.at`, rather than `squarePath`.
|
|
553
630
|
|
|
554
|
-
External consumers must
|
|
631
|
+
External consumers must adapt their code before taking this source version. Editing source does not update
|
|
555
632
|
an installed plugin, build, or running extension.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrosheep/square",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"node": "^22.16.0 || >=24.0.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@getpaseo/client": "0.
|
|
44
|
+
"@getpaseo/client": "0.14.0",
|
|
45
45
|
"@opencode/client": "2.0.20",
|
|
46
46
|
"@types/cross-spawn": "^6.0.6",
|
|
47
47
|
"@types/node": "^22",
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
},
|
|
102
102
|
"peerDependencies": {
|
|
103
103
|
"@earendil-works/pi-tui": "*",
|
|
104
|
-
"@getpaseo/client": "0.
|
|
104
|
+
"@getpaseo/client": "0.14.0",
|
|
105
105
|
"ws": "^8.21.3"
|
|
106
106
|
},
|
|
107
107
|
"peerDependenciesMeta": {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type SquareInboxBinding = { location: string; participant: string; session: string; generation?:
|
|
1
|
+
export type SquareInboxBinding = { location: string; participant: string; session: string; generation?: string };
|
|
2
2
|
export type SquareInboxCoordinate = { sessionId: string; cwd: string; version: string; endpoint?: string };
|
|
3
3
|
export type SquareInboxCancellation = { at: number; bindings?: SquareInboxBinding[] };
|
|
4
4
|
export type SquareInboxState = { coordinate: SquareInboxCoordinate; bindings?: SquareInboxBinding[] };
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import {
|
|
2
|
+
import { squareWakeProjection } from '../../wake-preview.js';
|
|
3
|
+
import { channelArtifact, createArtifactPort } from '../../artifact.js';
|
|
3
4
|
import { deliverPending } from '../../delivery-operations.js';
|
|
4
|
-
import {
|
|
5
|
-
import { automaticWakeEligible, presentationBlocksWake } from '../../delivery.js';
|
|
5
|
+
import { nativePendingSource, observeSessionPending, projectSessionSeats } from '../../inbox.js';
|
|
6
|
+
import { automaticWakeEligible, nativePendingHead, presentationBlocksWake } from '../../delivery.js';
|
|
6
7
|
import { type Seat } from '../../model.js';
|
|
7
8
|
import { harnessOwner, decodeHarnessOwner } from '../../owner.js';
|
|
8
|
-
import type {
|
|
9
|
-
import {
|
|
9
|
+
import type { WakeRequest, WakeTransportPort } from '../../ports.js';
|
|
10
|
+
import { formatMessageId, parseMessageId } from '../../message.js';
|
|
10
11
|
import { indexedSquareEntries } from '../../square-index.js';
|
|
11
12
|
import { asSeatRef } from '../../seats.js';
|
|
12
13
|
import { restoreSessionSeats } from '../../session-lifecycle.js';
|
|
@@ -18,17 +19,6 @@ const ownerFor = (sessionId: string) => harnessOwner({ channel: 'opencode', sess
|
|
|
18
19
|
const payloadHash = (payload: string) => createHash('sha256').update(payload).digest('hex');
|
|
19
20
|
export interface OpenCodeContextMessage { readonly id?: string; readonly role: string; readonly content: readonly { readonly type: string; readonly text?: string }[] }
|
|
20
21
|
|
|
21
|
-
function preview(facts: SquareRead, location: string, captured: Pick<Seat, 'participant' | 'owner' | 'generation'>, index: number) {
|
|
22
|
-
const seat = facts.seat(captured.participant);
|
|
23
|
-
if (seat === undefined || seat.attachment !== 'attached' || seat.generation !== captured.generation || seat.owner !== captured.owner) return undefined;
|
|
24
|
-
const wake = seat.wakeAddress;
|
|
25
|
-
if (wake?.kind !== routeKind) return undefined;
|
|
26
|
-
return nativePendingPreview(facts, {
|
|
27
|
-
location, routeKind, generation: seat.generation, participant: seat.participant,
|
|
28
|
-
actIndex: index, address: wake.address, now: Date.now(),
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
22
|
export async function indexedOpenCodeSessions(env: NodeJS.ProcessEnv): Promise<readonly string[]> {
|
|
33
23
|
return [...new Set((await indexedSquareEntries({ }, env)).flatMap((entry) => {
|
|
34
24
|
const coordinate = decodeHarnessOwner(entry.owner);
|
|
@@ -49,9 +39,8 @@ function createOpenCodeWakeTransport(target: OpenCodeNativeSession, lifetime: Ab
|
|
|
49
39
|
probe: async (route) => !lifetime.aborted && route.kind === routeKind
|
|
50
40
|
&& route.address.sessionId === target.sessionId,
|
|
51
41
|
async attempt(request: WakeRequest, timeoutMs: number, beforeSend) {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
if (artifact === undefined || request.claimToken === undefined || index === undefined
|
|
42
|
+
const presentation = request.presentation;
|
|
43
|
+
if (request.claimToken === undefined || parseMessageId(request.message) === undefined || presentation === undefined
|
|
55
44
|
|| request.route.kind !== routeKind || request.route.address.sessionId !== target.sessionId) {
|
|
56
45
|
return { outcome: 'not-capable', diagnostic: 'OpenCode attention has no matching artifact claim or session.' };
|
|
57
46
|
}
|
|
@@ -59,18 +48,14 @@ function createOpenCodeWakeTransport(target: OpenCodeNativeSession, lifetime: Ab
|
|
|
59
48
|
const started = Date.now();
|
|
60
49
|
if (signal.aborted || !await beforeSend())
|
|
61
50
|
return { outcome: 'not-capable', diagnostic: 'OpenCode attention changed before send.' };
|
|
62
|
-
const { value: prepared } = await artifact.read((facts) => preview(facts, request.location, request.seat, index), signal);
|
|
63
|
-
if (prepared === undefined) return { outcome: 'not-capable', diagnostic: 'OpenCode attention is no longer current.' };
|
|
64
51
|
const inputId = createNativeInputId();
|
|
65
|
-
const correlation = { harness: 'opencode' as const, token: inputId, payloadHash: payloadHash(
|
|
66
|
-
if (!await
|
|
52
|
+
const correlation = { harness: 'opencode' as const, token: inputId, payloadHash: payloadHash(presentation.payload) };
|
|
53
|
+
if (!await request.channel.prepare({ correlation,
|
|
54
|
+
coverage: presentation.coverage, ...(presentation.description === undefined ? {} : { description: presentation.description }) }, signal))
|
|
67
55
|
return { outcome: 'not-capable', diagnostic: 'OpenCode delivery claim is no longer current.' };
|
|
68
|
-
const fresh = await artifact.read((facts) => preview(facts, request.location, request.seat, index), signal);
|
|
69
|
-
if (fresh.value === undefined || payloadHash(fresh.value.payload) !== correlation.payloadHash)
|
|
70
|
-
return { outcome: 'not-capable', diagnostic: 'OpenCode attention changed before native send.' };
|
|
71
56
|
if (signal.aborted || !await beforeSend())
|
|
72
57
|
return { outcome: 'not-capable', diagnostic: 'OpenCode attention was consumed before send.' };
|
|
73
|
-
const result = await sendNativeText(target,
|
|
58
|
+
const result = await sendNativeText(target, presentation.payload, {
|
|
74
59
|
inputId, delivery: 'steer', timeoutMs: Math.max(1, timeoutMs - (Date.now() - started)), signal,
|
|
75
60
|
});
|
|
76
61
|
if (result.state === 'accepted') return { outcome: 'accepted' };
|
|
@@ -101,25 +86,27 @@ export async function observeOpenCodeContext(sessionId: string, messages: readon
|
|
|
101
86
|
return facts.deliveryEvidence(seat.participant, seat.generation).flatMap((evidence) => {
|
|
102
87
|
const token = evidence.correlation?.harness === 'opencode' ? evidence.correlation.token : undefined;
|
|
103
88
|
const hash = token === undefined ? undefined : observed.get(token);
|
|
104
|
-
const index =
|
|
89
|
+
const index = parseMessageId(evidence.message), now = Date.now();
|
|
105
90
|
if (token === undefined || hash === undefined || evidence.claimToken === undefined || index === undefined
|
|
106
|
-
|| !['dispatching', 'unknown', 'accepted'].includes(evidence.outcome)
|
|
107
|
-
|| evidence.correlation?.payloadHash !== hash
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
91
|
+
|| evidence.route === undefined || !['dispatching', 'unknown', 'accepted'].includes(evidence.outcome)
|
|
92
|
+
|| evidence.correlation?.payloadHash !== hash
|
|
93
|
+
|| evidence.route.kind !== routeKind
|
|
94
|
+
|| presentationBlocksWake(facts.deliveryEvidence(seat.participant, seat.generation, evidence.message), now)) return [];
|
|
95
|
+
const source = nativePendingSource(facts, { location, participant: seat.participant, generation: seat.generation,
|
|
96
|
+
owner: seat.owner, index, route: evidence.route, now });
|
|
97
|
+
if (source === undefined) return [];
|
|
98
|
+
return [{ evidence, index, hash, clipped: evidence.coverage === 'clipped' }];
|
|
112
99
|
});
|
|
113
100
|
}, signal);
|
|
114
101
|
for (const { evidence, index, hash, clipped } of matches) {
|
|
115
102
|
if (signal.aborted || evidence.claimToken === undefined) return;
|
|
116
103
|
await presentPending({ artifact, location, participant: seat.participant,
|
|
117
|
-
|
|
104
|
+
message: formatMessageId(index), seat: asSeatRef(seat), observationOf: evidence.claimToken, sink: { present() {} }, markSeen: !clipped, signal,
|
|
118
105
|
current: (facts) => {
|
|
119
|
-
const current = facts.seat(seat.participant);
|
|
120
|
-
const prepared = current === undefined || !sameSeat(current, seat) ? undefined : preview(facts, location, seat, index);
|
|
121
106
|
const correlation = facts.deliveryByToken(evidence.claimToken!);
|
|
122
|
-
return
|
|
107
|
+
return nativePendingHead(facts, { location, participant: seat.participant, generation: seat.generation,
|
|
108
|
+
owner: seat.owner, index, route: evidence.route!, now: Date.now() }) !== undefined
|
|
109
|
+
&& ['dispatching', 'unknown', 'accepted'].includes(correlation?.outcome ?? '')
|
|
123
110
|
&& correlation?.correlation?.harness === 'opencode'
|
|
124
111
|
&& correlation.correlation.token !== undefined
|
|
125
112
|
&& correlation.correlation.payloadHash === hash;
|
|
@@ -150,11 +137,11 @@ export async function receiveOpenCodePending(target: OpenCodeNativeSession, env:
|
|
|
150
137
|
const { value: notifications } = await artifact.read((facts) => {
|
|
151
138
|
if (!sameSeat(facts.seat(seat.participant), seat)) return [];
|
|
152
139
|
return membership.notifications.filter((note) => automaticWakeEligible(
|
|
153
|
-
facts.deliveryEvidence(seat.participant, seat.generation,
|
|
140
|
+
facts.deliveryEvidence(seat.participant, seat.generation, formatMessageId(note.messageIndex)), Date.now()));
|
|
154
141
|
}, signal);
|
|
155
142
|
for (const note of notifications) {
|
|
156
143
|
if (signal.aborted) return;
|
|
157
|
-
await deliverPending({ artifact, transport, location,
|
|
144
|
+
await deliverPending({ artifact: channelArtifact(artifact), preparePresentation: squareWakeProjection(artifact), transport, location, message: formatMessageId(note.messageIndex), now: Date.now() });
|
|
158
145
|
}
|
|
159
146
|
} catch (error) {
|
|
160
147
|
if (signal.aborted) throw signal.reason ?? error;
|
|
@@ -194,7 +181,7 @@ export async function cancelOpenCodeDeliveries(sessionId: string, env: NodeJS.Pr
|
|
|
194
181
|
if (seat.attachment !== 'attached') continue;
|
|
195
182
|
const artifact = createArtifactPort(location, undefined, signal);
|
|
196
183
|
try {
|
|
197
|
-
const { value: through } = await artifact.read((facts) => facts.header().
|
|
184
|
+
const { value: through } = await artifact.read((facts) => facts.channel.header().lastMessageIndex, signal);
|
|
198
185
|
if (through >= 0) await artifact.cancelDeliveries(asSeatRef(seat), through, signal);
|
|
199
186
|
} finally { await artifact.close(); }
|
|
200
187
|
}
|
package/plugins/pi/square-pi.js
CHANGED
|
@@ -3,14 +3,15 @@ import { Container, Markdown, Spacer, Text } from '@earendil-works/pi-tui';
|
|
|
3
3
|
import { z } from 'zod/v4';
|
|
4
4
|
|
|
5
5
|
import { Square, harnessOwner } from '../../dist/square-api.js';
|
|
6
|
-
import { SQUARE_TOOLS, SQUARE_TOOL_INSTRUCTIONS,
|
|
6
|
+
import { SQUARE_TOOLS, SQUARE_TOOL_INSTRUCTIONS, createSquareToolRunner, squareToolsEnabled } from '../../dist/square-tools.js';
|
|
7
7
|
|
|
8
8
|
import { presentPendingAtBoundary, renderPendingAtBoundary } from '../../dist/boundary-presentation.js';
|
|
9
9
|
import { operationEnv } from '../../dist/participant-identity.js';
|
|
10
10
|
import { detachSessionSeats, restoreSessionSeats } from '../../dist/session-lifecycle.js';
|
|
11
11
|
import { projectSessionSeats, observeSessionPending, pendingKey } from '../../dist/inbox.js';
|
|
12
|
-
import {
|
|
12
|
+
import { projectMessageNotification } from '../../dist/attention-presentation.js';
|
|
13
13
|
import { formatActivityId } from '../../dist/square-core.js';
|
|
14
|
+
import { formatMessageId } from '../../dist/message.js';
|
|
14
15
|
import { createPiReceiver, sendPiMessage } from '../../dist/packages/agent-delivery/src/pi.js';
|
|
15
16
|
|
|
16
17
|
class PiDeliveryDroppedError extends Error {
|
|
@@ -26,7 +27,7 @@ export function pendingInbox(inbox) {
|
|
|
26
27
|
|
|
27
28
|
export function inboxKeys(inbox) {
|
|
28
29
|
return pendingInbox(inbox).flatMap((item) => item.notifications.map((note) =>
|
|
29
|
-
pendingKey(item, note.
|
|
30
|
+
pendingKey(item, note.messageIndex)
|
|
30
31
|
));
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -40,25 +41,26 @@ function framePiMessage(content) {
|
|
|
40
41
|
|
|
41
42
|
/** Renderer-only card facts for Pi and Paseo; Pi never sends `details` to the model. */
|
|
42
43
|
export function pendingCardDetails(pending) {
|
|
43
|
-
const items = pending.flatMap((membership) => membership.notifications.map((note) =>
|
|
44
|
+
const items = pending.flatMap((membership) => membership.notifications.map((note) => {
|
|
45
|
+
const projected = projectMessageNotification({ squarePath: membership.location, recipient: membership.seat.participant,
|
|
46
|
+
actor: note.actor, body: note.body, ...(note.preview === undefined ? {} : { preview: note.preview }),
|
|
47
|
+
...(note.actIndex === undefined || note.route === undefined ? {} : { agora: { actIndex: note.actIndex, route: note.route } }) });
|
|
48
|
+
return ({
|
|
44
49
|
square: path.basename(membership.location, '.square'),
|
|
45
50
|
actor: note.actor,
|
|
46
51
|
recipient: membership.seat.participant,
|
|
47
52
|
route: note.route,
|
|
48
|
-
act: formatActivityId(note.actIndex),
|
|
49
|
-
preview:
|
|
50
|
-
description:
|
|
51
|
-
? `${note.actor} → ${membership.seat.participant}`
|
|
52
|
-
: renderAttentionDescription({ actor: note.actor, route: note.route }),
|
|
53
|
+
act: note.actIndex === undefined ? formatMessageId(note.messageIndex) : formatActivityId(note.actIndex),
|
|
54
|
+
preview: projected.preview,
|
|
55
|
+
description: projected.description,
|
|
53
56
|
body: note.body,
|
|
54
|
-
})));
|
|
57
|
+
}); }));
|
|
55
58
|
if (items.length === 0) return undefined;
|
|
56
|
-
const fullText = pending.flatMap((membership) => membership.notifications.map((note) =>
|
|
57
|
-
squarePath: membership.location,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}))).join('\n\n');
|
|
59
|
+
const fullText = pending.flatMap((membership) => membership.notifications.map((note) => projectMessageNotification({
|
|
60
|
+
squarePath: membership.location, recipient: membership.seat.participant, actor: note.actor, body: note.body,
|
|
61
|
+
...(note.preview === undefined ? {} : { preview: note.preview }),
|
|
62
|
+
...(note.actIndex === undefined || note.route === undefined ? {} : { agora: { actIndex: note.actIndex, route: note.route } }),
|
|
63
|
+
}).full)).join('\n\n');
|
|
62
64
|
return { items, fullText };
|
|
63
65
|
}
|
|
64
66
|
|
|
@@ -143,7 +145,7 @@ export function renderSquareCard(message, { expanded, outputPad = 1 }, theme) {
|
|
|
143
145
|
}
|
|
144
146
|
|
|
145
147
|
export default function squarePiExtension(pi) {
|
|
146
|
-
const
|
|
148
|
+
const invoke = createSquareToolRunner();
|
|
147
149
|
if (squareToolsEnabled(process.env)) for (const tool of SQUARE_TOOLS) {
|
|
148
150
|
pi.registerTool({
|
|
149
151
|
name: `square_${tool.name}`,
|
|
@@ -153,41 +155,16 @@ export default function squarePiExtension(pi) {
|
|
|
153
155
|
exposure: 'codemode',
|
|
154
156
|
namespace: { name: 'square', description: 'A shared square where agents gather to talk, think, and act together.', instructions: SQUARE_TOOL_INSTRUCTIONS },
|
|
155
157
|
async execute(_id, args, signal, _onUpdate, ctx) {
|
|
156
|
-
|
|
157
|
-
let owner;
|
|
158
|
-
let draftToken;
|
|
159
|
-
let expressAttempted = false;
|
|
160
|
-
let square;
|
|
161
|
-
try {
|
|
158
|
+
const { text, isError } = await invoke(tool, args, { signal }, () => {
|
|
162
159
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
env: operationEnv('pi', sessionId, process.env),
|
|
172
|
-
path: toolArgs.location,
|
|
173
|
-
}, { signal });
|
|
174
|
-
let text;
|
|
175
|
-
try {
|
|
176
|
-
expressAttempted = tool.name === 'express';
|
|
177
|
-
text = await tool.run(square, toolArgs, { signal }, { owner });
|
|
178
|
-
drafts.consume(draftToken);
|
|
179
|
-
}
|
|
180
|
-
finally { await square.close(); }
|
|
181
|
-
return { content: [{ type: 'text', text }] };
|
|
182
|
-
} catch (error) {
|
|
183
|
-
const failure = squareToolFailure(error, signal);
|
|
184
|
-
const draft = tool.name === 'express' ? drafts.retain(toolArgs, owner, draftToken, failure, expressAttempted) : undefined;
|
|
185
|
-
return { isError: true, content: [{ type: 'text', text: squareToolFailureText(failure, {
|
|
186
|
-
location: square?.location ?? toolArgs.location ?? process.env.SQUARE_LOCATION,
|
|
187
|
-
participant: toolArgs.as,
|
|
188
|
-
draft,
|
|
189
|
-
}) }] };
|
|
190
|
-
}
|
|
160
|
+
return {
|
|
161
|
+
identity: { owner: harnessOwner({ channel: 'pi', session: sessionId }) },
|
|
162
|
+
open: (location, control) => Square.at({
|
|
163
|
+
cwd: ctx.cwd, env: operationEnv('pi', sessionId, process.env), path: location,
|
|
164
|
+
}, control),
|
|
165
|
+
};
|
|
166
|
+
}, process.env.SQUARE_LOCATION);
|
|
167
|
+
return { ...(isError ? { isError } : {}), content: [{ type: 'text', text }] };
|
|
191
168
|
},
|
|
192
169
|
});
|
|
193
170
|
}
|