@dopamint-fun/open-sdk 0.2.0-dev.7 → 0.2.0-dev.9

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/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { deriveAgentId, registerCanonicalPayload, registerPayloadDigest, type Re
3
3
  export { canonicalIdentityPayload, nameAgent, parseIdentityFields, type AgentIdentityFields, type NameAgentArgs, } from "./identity.js";
4
4
  export { actionSigningBytes, encodeActionFrame, encodeJoinFrame, encodeResumeFrame, joinSigningBytes, resumeSigningBytes, SESSION_VERSION, UnsupportedSessionVersionError, type ActionProposal, type ArtifactReference, type JoinRequest, type ResumeRequest, type SessionContext, } from "./sessionWire.js";
5
5
  export { encodeAckFrame, SESSION_ERROR_NAMES, MAX_PREDICTION_PACING_MS, admitAuthorityEvent, applyPredictionGateStatus, decodeAuthorityMessage, freshSessionBoundary, predictionGateMessage, predictionGatePreparationOf, predictionGateTargetReceipt, sessionErrorHint, sessionErrorName, PredictionGateConflictError, SessionBoundaryError, type AcceptedSessionBoundary, type AuthorityMessage, type PredictionGateMessage, type PredictionGateOpening, type PredictionGatePreparation, type PredictionGateRelease, type PredictionGateStatus, type PredictionWindowRef, type ReceiptRef, type ResumeCursor, type StateRef, type ViewSnapshot, } from "./sessionCodec.js";
6
- export { type OpenTableView, type PlayReport, type SeatDecision, type SeatDecisionResult, type SeatPosition, SessionClient, SessionRefusal, chooseSeatAction, playSeat, } from "./session.js";
6
+ export { type OpenTableView, type PlayReport, type SeatDecision, type SeatDecisionResult, type SeatPosition, SessionClient, SessionRefusal, chooseSeatAction, playSeat, waitForReadyOwner, } from "./session.js";
7
7
  export { cardFromByte, decodeLegalActions, decodeParticipantView, encodeAction, pickAction, type Card, type Rank, type Suit, type TexasAction, type TexasLegalActions, type TexasSeatView, } from "./texas.js";
8
8
  export { buildConsentRequest, DEFAULT_OPEN_API_BASE_URL, digestForPrompt, recomputeSettlementDigest, settlementConsentPath, verifyConsentDisclosure, } from "./settlement.js";
9
9
  export { PACKAGE_NAME, RELEASE_CHANNELS, channelDistTag, installCommand, installSpec, resolveChannel, versionMatchesChannel, type ReleaseChannel, } from "./channel.js";
@@ -18,4 +18,4 @@ export { cardIndex, handCategory, scoreHand, HAND_CATEGORIES, RANK_ORDER, SUIT_O
18
18
  export { DEFAULT_SEAT_STATE_FILE, loadSeatState, saveSeatState, type SeatSessionState, } from "./seatState.js";
19
19
  export { newSeatState, openTurn, submitTurn, type SeatStateCheckpoint, type SeatTurnOutcome, type SeatTurnPosition, } from "./seatTurn.js";
20
20
  export { chipAddressOf, joinTransaction, leaveTransaction, listTournaments, planJoin, presentToTournament, readAgentEntry, readAgentEntrySettled, BOOK_CATCHES_UP_MS, readPass, readOwner, playsHeldBy, giveBackTransaction, readTournament, sponsorAndExecute, tournamentIdArg, tournamentPossessionSignature, JoinRefused, TournamentRefusal, TOURNAMENT_POSSESSION_SEAT, type JoinPlan, type SponsoredExecution, type TournamentAgentEntry, type TournamentPass, type TournamentPlay, type TournamentOwnerView, type TournamentOwnerAgent, type TournamentChainObjects, type TournamentClient, type TournamentOverview, type TournamentQueueEntry, } from "./openTournament.js";
21
- export { ClientRefusal, describeNext, refusalLines, refusalMessage, refusalMessageFromText, type RefusalBody, type RefusalNext, } from "./refusal.js";
21
+ export { ClientRefusal, describeNext, ownershipFencePoll, ownershipFenced, refusalLines, refusalMessage, refusalMessageFromText, type RefusalBody, type RefusalNext, } from "./refusal.js";
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ export { deriveAgentId, registerCanonicalPayload, registerPayloadDigest, } from
10
10
  export { canonicalIdentityPayload, nameAgent, parseIdentityFields, } from "./identity.js";
11
11
  export { actionSigningBytes, encodeActionFrame, encodeJoinFrame, encodeResumeFrame, joinSigningBytes, resumeSigningBytes, SESSION_VERSION, UnsupportedSessionVersionError, } from "./sessionWire.js";
12
12
  export { encodeAckFrame, SESSION_ERROR_NAMES, MAX_PREDICTION_PACING_MS, admitAuthorityEvent, applyPredictionGateStatus, decodeAuthorityMessage, freshSessionBoundary, predictionGateMessage, predictionGatePreparationOf, predictionGateTargetReceipt, sessionErrorHint, sessionErrorName, PredictionGateConflictError, SessionBoundaryError, } from "./sessionCodec.js";
13
- export { SessionClient, SessionRefusal, chooseSeatAction, playSeat, } from "./session.js";
13
+ export { SessionClient, SessionRefusal, chooseSeatAction, playSeat, waitForReadyOwner, } from "./session.js";
14
14
  export { cardFromByte, decodeLegalActions, decodeParticipantView, encodeAction, pickAction, } from "./texas.js";
15
15
  export { buildConsentRequest, DEFAULT_OPEN_API_BASE_URL, digestForPrompt, recomputeSettlementDigest, settlementConsentPath, verifyConsentDisclosure, } from "./settlement.js";
16
16
  export { PACKAGE_NAME, RELEASE_CHANNELS, channelDistTag, installCommand, installSpec, resolveChannel, versionMatchesChannel, } from "./channel.js";
@@ -30,4 +30,4 @@ export { cardIndex, handCategory, scoreHand, HAND_CATEGORIES, RANK_ORDER, SUIT_O
30
30
  export { DEFAULT_SEAT_STATE_FILE, loadSeatState, saveSeatState, } from "./seatState.js";
31
31
  export { newSeatState, openTurn, submitTurn, } from "./seatTurn.js";
32
32
  export { chipAddressOf, joinTransaction, leaveTransaction, listTournaments, planJoin, presentToTournament, readAgentEntry, readAgentEntrySettled, BOOK_CATCHES_UP_MS, readPass, readOwner, playsHeldBy, giveBackTransaction, readTournament, sponsorAndExecute, tournamentIdArg, tournamentPossessionSignature, JoinRefused, TournamentRefusal, TOURNAMENT_POSSESSION_SEAT, } from "./openTournament.js";
33
- export { ClientRefusal, describeNext, refusalLines, refusalMessage, refusalMessageFromText, } from "./refusal.js";
33
+ export { ClientRefusal, describeNext, ownershipFencePoll, ownershipFenced, refusalLines, refusalMessage, refusalMessageFromText, } from "./refusal.js";
package/dist/refusal.d.ts CHANGED
@@ -29,6 +29,11 @@ export interface RefusalBody {
29
29
  docs?: string;
30
30
  }
31
31
  /** `next …`, as one line an agent can act on. */
32
+ /** True when a refusal body says the writer is fenced and the caller should
33
+ * poll committed ownership rather than resend to the same origin. */
34
+ export declare function ownershipFenced(body: unknown): boolean;
35
+ /** The product route a fenced seat polls, when the refusal named one. */
36
+ export declare function ownershipFencePoll(body: unknown): string | undefined;
32
37
  export declare function describeNext(next: RefusalNext): string;
33
38
  /** The lines a refusal prints: its code, what it said, the move, the entry. */
34
39
  export declare function refusalLines(body: unknown): string[];
package/dist/refusal.js CHANGED
@@ -6,6 +6,27 @@
6
6
  * reading a refused command's output reads its move off the same lines rather
7
7
  * than off a table in a skill document. */
8
8
  /** `next …`, as one line an agent can act on. */
9
+ /** True when a refusal body says the writer is fenced and the caller should
10
+ * poll committed ownership rather than resend to the same origin. */
11
+ export function ownershipFenced(body) {
12
+ if (typeof body !== "object" || body === null)
13
+ return false;
14
+ const refusal = body;
15
+ if (refusal.code !== "ownership_fenced")
16
+ return false;
17
+ return (isNext(refusal.next) &&
18
+ refusal.next.action === "wait" &&
19
+ typeof refusal.next.poll === "string");
20
+ }
21
+ /** The product route a fenced seat polls, when the refusal named one. */
22
+ export function ownershipFencePoll(body) {
23
+ if (!ownershipFenced(body))
24
+ return undefined;
25
+ const next = body.next;
26
+ if (next && next.action === "wait")
27
+ return next.poll;
28
+ return undefined;
29
+ }
9
30
  export function describeNext(next) {
10
31
  switch (next.action) {
11
32
  case "retry":
package/dist/session.d.ts CHANGED
@@ -327,6 +327,18 @@ export interface RestartRetry {
327
327
  * answer, 408, 429 or a 5xx -- until `bound` has passed. Any other answer is
328
328
  * returned as it came, so a refusal stays the caller's to read. */
329
329
  export declare function fetchWhileRestarting(fetchImpl: typeof fetch, url: string, retry?: RestartRetry): Promise<Response>;
330
+ /** Poll product until the committed owner is ready, then return its origin. */
331
+ export declare function waitForReadyOwner(args: {
332
+ productUrl: string;
333
+ executionId: string;
334
+ fetchImpl?: typeof fetch;
335
+ timeoutMs?: number;
336
+ pauseMs?: number;
337
+ sleep?: (ms: number) => Promise<void>;
338
+ }): Promise<{
339
+ sessionOrigin: string;
340
+ version: number;
341
+ }>;
330
342
  export declare function openSeatSession(args: SeatSessionArgs): Promise<SeatSession>;
331
343
  export declare function playSeat(args: PlayArgs): Promise<PlayReport>;
332
344
  /** What the play loop does about a refusal, by the refusal's tag.
package/dist/session.js CHANGED
@@ -7,6 +7,7 @@ import { MAX_TRANSPORT_FRAME_BYTES, PredictionGateConflictError, SessionBoundary
7
7
  import { actionSigningBytes, encodeActionFrame, encodeJoinFrame, encodeResumeFrame, joinSigningBytes, requireSessionVersion, resumeSigningBytes, SESSION_VERSION, UnsupportedSessionVersionError, } from "./sessionWire.js";
8
8
  import { decodeLegalActions, decodeParticipantView, encodeAction, pickAction, } from "./texas.js";
9
9
  import { authorizeSeatChallenge } from "./seatAuth.js";
10
+ import { ownershipFenced } from "./refusal.js";
10
11
  const ACTION_IDENTITY_DOMAIN = textBytes("dopa_open::client::action_identity_v1");
11
12
  /** The lines a play door reports for one seat's sitting.
12
13
  *
@@ -528,6 +529,43 @@ export async function fetchWhileRestarting(fetchImpl, url, retry = {
528
529
  await sleep(retry.pauseMs);
529
530
  }
530
531
  }
532
+ /** Poll product until the committed owner is ready, then return its origin. */
533
+ export async function waitForReadyOwner(args) {
534
+ const fetchImpl = args.fetchImpl ?? fetch;
535
+ const sleep = args.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
536
+ const product = args.productUrl.replace(/\/$/, "");
537
+ const until = Date.now() + (args.timeoutMs ?? 30_000);
538
+ const pause = args.pauseMs ?? 200;
539
+ const id = args.executionId.replace(/^0x/i, "");
540
+ for (;;) {
541
+ const response = await fetchImpl(`${product}/v1/authority/executions/${id}/route`);
542
+ if (response.ok) {
543
+ const body = (await response.json());
544
+ if (body.ready && body.sessionOrigin)
545
+ return { sessionOrigin: body.sessionOrigin, version: body.version ?? 0 };
546
+ }
547
+ else {
548
+ await response.body?.cancel();
549
+ }
550
+ if (Date.now() + pause > until)
551
+ throw new Error("committed owner did not become ready");
552
+ await sleep(pause);
553
+ }
554
+ }
555
+ function httpFailure(error) {
556
+ if (!(error instanceof Error))
557
+ return null;
558
+ const match = /^[a-z-]+ failed \((\d+)\): (.*)$/is.exec(error.message);
559
+ if (!match)
560
+ return null;
561
+ const status = Number(match[1]);
562
+ try {
563
+ return { status, body: JSON.parse(match[2] ?? "") };
564
+ }
565
+ catch {
566
+ return { status, body: match[2] ?? "" };
567
+ }
568
+ }
531
569
  export async function openSeatSession(args) {
532
570
  const fetchImpl = args.fetchImpl ?? fetch;
533
571
  const product = args.productUrl.replace(/\/$/, "");
@@ -581,7 +619,7 @@ export async function openSeatSession(args) {
581
619
  }
582
620
  export async function playSeat(args) {
583
621
  const fetchImpl = args.fetchImpl ?? fetch;
584
- const { client, product, executionHex, coordinatorKey, timeAuthorityKey, executionId, } = await openSeatSession(args);
622
+ let { client, product, executionHex, coordinatorKey, timeAuthorityKey, executionId, } = await openSeatSession(args);
585
623
  const record = {
586
624
  admission: {
587
625
  execution_id: executionId,
@@ -923,6 +961,22 @@ export async function playSeat(args) {
923
961
  client joined this seat with this key; fighting it back would evict
924
962
  a reconnect that may be the operator's own, so this one steps aside
925
963
  and says so. Everything else is transient, and resume is right. */
964
+ const failed = httpFailure(error);
965
+ if (failed?.status === 409 && ownershipFenced(failed.body)) {
966
+ const ready = await waitForReadyOwner({
967
+ productUrl: product,
968
+ executionId: executionHex,
969
+ fetchImpl,
970
+ timeoutMs: 30_000,
971
+ pauseMs: 200,
972
+ });
973
+ client = new SessionClient(ready.sessionOrigin, args.agent, args.seat, args.agentId, client.executionId, client.executionManifestDigest, client.clientNonce, fetchImpl);
974
+ client.token = originToken;
975
+ await client.resume(lastContext, lastCursor);
976
+ originToken = client.token ?? originToken;
977
+ reconnects += 1;
978
+ continue;
979
+ }
926
980
  const answer = afterRefusal(error);
927
981
  if (answer === "step-aside")
928
982
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dopamint-fun/open-sdk",
3
- "version": "0.2.0-dev.7",
3
+ "version": "0.2.0-dev.9",
4
4
  "description": "DOPA-OPEN client SDK: generate and hold your own agent key, sign registrations, and play a Participant Session",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {