@ref0/sdk 0.10.0 → 0.12.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/client.mjs CHANGED
@@ -5423,12 +5423,15 @@ const name = ownerNamePattern.source.replace(/^\^|\$$/g, "");
5423
5423
  const projectPattern = new RegExp(`^(${name})/(${name})$`);
5424
5424
  const threadPattern = new RegExp(`^(${name})(?:/(${name}))?#([1-9][0-9]*)$`);
5425
5425
  const repoPattern = new RegExp(`^(${name})/(${repoNamePattern.source.replace(/^\^|\$$/g, "")})$`);
5426
+ const pullRequestPattern = new RegExp(`${repoPattern.source.replace(/\$$/, "")}#[1-9][0-9]*$`);
5426
5427
  /** A project, `owner/project`. */
5427
5428
  const WrittenProject = Schema.String.check(Schema.isPattern(projectPattern)).pipe(Schema.brand("WrittenProject"));
5428
5429
  /** A thread, `owner/project#number`, or an org thread, `owner#number`. */
5429
5430
  const WrittenThread = Schema.String.check(Schema.isPattern(threadPattern)).pipe(Schema.brand("WrittenThread"));
5430
5431
  /** A repo, `owner/repo`. */
5431
5432
  const WrittenRepo = Schema.String.check(Schema.isPattern(repoPattern)).pipe(Schema.brand("WrittenRepo"));
5433
+ /** A pull request, `owner/repo#number`. */
5434
+ const WrittenPullRequest = Schema.String.check(Schema.isPattern(pullRequestPattern)).pipe(Schema.brand("WrittenPullRequest"));
5432
5435
  /** A written project's parts, as an endpoint's path takes them. */
5433
5436
  const projectParts = (written) => {
5434
5437
  const [, owner = "", project = ""] = projectPattern.exec(written) ?? [];
@@ -5437,6 +5440,15 @@ const projectParts = (written) => {
5437
5440
  project
5438
5441
  };
5439
5442
  };
5443
+ /** A written thread's parts, as an endpoint's path takes them: no project for an org thread. */
5444
+ const threadParts = (written) => {
5445
+ const [, owner = "", project, number = ""] = threadPattern.exec(written) ?? [];
5446
+ return {
5447
+ owner,
5448
+ project,
5449
+ number
5450
+ };
5451
+ };
5440
5452
  //#endregion
5441
5453
  //#region ../contracts/src/mention-views.ts
5442
5454
  /**
@@ -6493,14 +6505,13 @@ const ConnectOrganization = Schema.Struct({
6493
6505
  organization: OrgId,
6494
6506
  name: OwnerName
6495
6507
  });
6496
- /** Whether admission replaced a live host grant, or skipped one that no longer belongs here. */
6508
+ /**
6509
+ * A folder's host grant from before: on and the person's, so the new grant's confirm turns it off,
6510
+ * or skipped, off already or not theirs, which a yes leaves as it is.
6511
+ */
6497
6512
  const GrantReplacement = Schema.Struct({
6498
6513
  grant: TokenId,
6499
- state: Schema.Literals([
6500
- "replaces",
6501
- "revoked",
6502
- "skipped"
6503
- ]),
6514
+ state: Schema.Literals(["replaces", "skipped"]),
6504
6515
  attachedAt: Schema.optionalKey(Timestamp)
6505
6516
  });
6506
6517
  /**
@@ -6676,594 +6687,227 @@ Schema.Union([Schema.Struct({ found: Schema.Literal(false) }), Schema.Struct({
6676
6687
  attachGrant: Schema.optionalKey(EventId)
6677
6688
  })]);
6678
6689
  //#endregion
6679
- //#region ../contracts/src/approvals.ts
6690
+ //#region ../contracts/src/plan-events.ts
6680
6691
  /**
6681
- * Approvals, as their commands travel: what a terminal, a page or an agent asks its person to
6682
- * say yes to. A command is one endpoint, by its name in the surface table, and its request, as
6683
- * that endpoint encodes it. ref0 decodes each part by the endpoint's own `Schema` before it
6684
- * keeps or shows anything, so a command is exactly what the endpoint would take.
6692
+ * A plan's events, in its thread's project's log, written by the Project DO. A version's
6693
+ * state is folded from these and its thread's, never recorded on its own.
6685
6694
  */
6686
6695
  /**
6687
- * An endpoint by its name in the surface table, `group.endpoint`. Checked against the table
6688
- * where the command is made canonical, since the table is typed from the API this is part of.
6696
+ * A new version, as a draft. Its content never changes after this. Its reason, when given, says
6697
+ * why it was written; it's beside the plan, never in it, so it's no part of the content hash.
6689
6698
  */
6690
- const CommandEndpoint = Schema.String.check(Schema.isPattern(/^[a-zA-Z]+\.[a-zA-Z]+$/));
6691
- /** One command, as asked: its endpoint, and its request's path, query and payload, encoded. */
6692
- const ApprovalCommand = Schema.Struct({
6693
- endpoint: CommandEndpoint,
6694
- params: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
6695
- query: Schema.optionalKey(Schema.JsonObject),
6696
- payload: Schema.optionalKey(Schema.Json)
6697
- });
6698
- /** A command ref0 won't ask anyone about: not an endpoint's request, or not one an approval runs. */
6699
- var CommandRefused = class extends Schema.TaggedError()("CommandRefused", { nextStep: Schema.String }, { httpApiStatus: 400 }) {};
6700
- /** A terminal asking: its command, and the hash of the secret it collects the result with. */
6701
- const StartApproval = Schema.Struct({
6702
- command: ApprovalCommand,
6703
- secretHash: SecretHash
6704
- });
6705
- /** A terminal's approval, started: what to show its person, and until when. */
6706
- const ApprovalStarted = Schema.Struct({
6707
- approval: ApprovalId,
6708
- code: ApprovalCode,
6709
- expiresAt: Timestamp
6710
- });
6699
+ const PlanWritten = eventKind("plan.written", Schema.Struct({
6700
+ thread: ThreadId,
6701
+ version: PlanVersion,
6702
+ plan: PlanRecord,
6703
+ contentHash: PlanContentHash,
6704
+ reason: Schema.optionalKey(TextId)
6705
+ }));
6711
6706
  /**
6712
- * An answer that waits for the person: the call only the person may make, or that agents ask
6713
- * for, became an approval, which the person answers on their own page. A `202`, never a failure
6714
- * of the call: whoever asked collects the result with `GET /v1/approvals/{approval}`.
6707
+ * The version is put to approval: each repo it names is asked to answer for it, and ref0 says yes
6708
+ * once they have or their time runs out. Any earlier draft or proposed version is
6709
+ * superseded.
6715
6710
  */
6716
- var ApprovalNeeded = class extends Schema.TaggedError()("ApprovalNeeded", {
6717
- approval: ApprovalId,
6718
- says: Schema.String,
6719
- expiresAt: Timestamp
6720
- }, { httpApiStatus: 202 }) {};
6721
- const terminalAsker = Schema.Struct({ kind: Schema.Literal("terminal") });
6722
- const pageAsker = Schema.Struct({ kind: Schema.Literal("page") });
6723
- const agentAsker = Schema.Struct({
6724
- kind: Schema.Literal("agent"),
6725
- agent: AgentId,
6726
- handle: AgentHandle,
6727
- session: SessionId,
6728
- /** The attachment its credential was made under, for a person's agent in an organization. */
6729
- attachment: Schema.optionalKey(CredentialRef)
6711
+ const PlanProposed = eventKind("plan.proposed", Schema.Struct({
6712
+ thread: ThreadId,
6713
+ version: PlanVersion
6714
+ }));
6715
+ /** Who said yes: ref0's plans part, once every repo answered or its time ran out. */
6716
+ const PlanAcceptedBy = Schema.Struct({
6717
+ kind: Schema.Literal("answered"),
6718
+ part: Ref0Part
6719
+ });
6720
+ /** A child thread, by the plan entry that made it. */
6721
+ const PlanChildThread = Schema.Struct({
6722
+ entry: PlanEntryKey,
6723
+ thread: ThreadId
6730
6724
  });
6725
+ const ChildThreads = Schema.Array(PlanChildThread).check(Schema.isMaxLength(50));
6731
6726
  /**
6732
- * Who asked, as a receipt keeps it: a terminal, one of ref0's pages, an agent, or a machine
6733
- * asking to connect, by its ID and its own word for itself, and from where.
6727
+ * The version is accepted, and the one accepted before it superseded. Each child is named only
6728
+ * once its owner has registered it.
6734
6729
  */
6735
- const ApprovalAsker = Schema.Union([
6736
- terminalAsker,
6737
- pageAsker,
6738
- Schema.Struct({
6739
- kind: Schema.Literal("machine"),
6740
- machine: MachineId,
6741
- machineName: MachineName,
6742
- askedFrom: Schema.optionalKey(Schema.String)
6743
- }),
6744
- agentAsker
6745
- ]);
6730
+ const PlanAccepted = eventKind("plan.accepted", Schema.Struct({
6731
+ thread: ThreadId,
6732
+ version: PlanVersion,
6733
+ by: PlanAcceptedBy,
6734
+ /** The version accepted before this one. */
6735
+ supersedes: Schema.optionalKey(PlanVersion),
6736
+ /** Children made now, each starting in planning. */
6737
+ started: ChildThreads,
6738
+ /** Entries an earlier version dropped and this one adds back: the same child, reopened if it was closed. */
6739
+ rejoined: ChildThreads,
6740
+ /** Children no longer part of the plan, so it stops waiting on them. Closing one is its own event. */
6741
+ dropped: ChildThreads
6742
+ }));
6743
+ //#endregion
6744
+ //#region ../contracts/src/plan-calls.ts
6746
6745
  /**
6747
- * Who asked, as the person reads a request before they answer: a terminal, a page or an agent.
6748
- * A machine's request is read as an `AttachmentRequestView`, never as a command.
6746
+ * Plans and decisions: what the Worker asks a thread's Project DO, once `Authorize` has said
6747
+ * who's acting. A plan is written with its words; the Project DO stores each as a text and
6748
+ * records the version with their IDs.
6749
6749
  */
6750
- const ApprovalViewAsker = Schema.Union([
6751
- terminalAsker,
6752
- pageAsker,
6753
- agentAsker
6754
- ]);
6755
- /** A request, as the person reads it before they answer. */
6756
- const ApprovalView = Schema.Struct({
6757
- approval: ApprovalId,
6758
- /** The command as its CLI line. */
6759
- says: Schema.String,
6760
- /** Everything the command carries, each named, in order: nothing it would run is left out. */
6761
- fields: Schema.Array(Schema.Struct({
6762
- name: Schema.String,
6763
- value: Schema.String,
6764
- part: Schema.Literals([
6765
- "path",
6766
- "query",
6767
- "payload"
6768
- ])
6769
- })),
6770
- command: ApprovalCommand,
6771
- asker: ApprovalViewAsker,
6772
- askedFrom: Schema.optionalKey(Schema.String),
6773
- askedAt: Timestamp,
6774
- expiresAt: Timestamp,
6775
- /**
6776
- * An agent's ask: the kind of action its answer may set agents' permission for, and whether a
6777
- * yes may let agents take it without asking. A no with the passkey may always stop them asking.
6778
- */
6779
- agentPermission: Schema.optionalKey(Schema.Struct({
6780
- action: ScopeAction,
6781
- mayAllow: Schema.Boolean
6782
- })),
6783
- /**
6784
- * What the command acts on, drawn as ref0 resolves it for the person reading, where its
6785
- * endpoint names a preview. Without one the page can draw, it offers only no.
6786
- */
6787
- preview: Schema.optionalKey(ApprovalPreview)
6788
- });
6789
- /** A statement's hash: the SHA-256 of its words and quoted values as JSON, as lowercase hex. */
6790
- const StatementHash = Schema.String.check(Schema.isPattern(/^[0-9a-f]{64}$/));
6750
+ /** A plan's free text: one of its outcomes, or an answer's reason. */
6751
+ const PlanText = Schema.String.check(Schema.isBetweenLength(1, 2048));
6791
6752
  /**
6792
- * Who asks for a yes to a statement: a workflow's run, `passkey.run`'s, by the workflow and the
6793
- * run; or a gate's check, `passkey.check`'s, by the check, the pull request's version it gates and
6794
- * the check run its statement is over.
6753
+ * How a plan does its work, in system terms, for people: about a page. A revision replaces it,
6754
+ * with a reason; no one appends to it.
6795
6755
  */
6796
- const StatementAsker = Schema.Union([Schema.Struct({
6797
- kind: Schema.Literal("workflow"),
6798
- workflow: WorkflowName,
6799
- run: WorkflowRunId
6800
- }), Schema.Struct({
6801
- kind: Schema.Literal("gate"),
6802
- check: CheckKey,
6803
- pullRequest: PullRequestNumber,
6804
- version: VersionNumber,
6805
- run: CheckRunId
6806
- })]);
6756
+ const Approach = Schema.String.check(Schema.isBetweenLength(1, 6e3));
6807
6757
  /**
6808
- * A passkey request, as the person reads it before they answer: the statement, its words as its
6809
- * asker wrote them and each value it quotes, and its hash; who asks; where, a workflow's
6810
- * `owner/repo` or a thread's `owner/project#number`, or a gate's pull request, `owner/repo#number`;
6811
- * whom it asks, the run's accountable person, or for a gate the person behind its run, a person by
6812
- * name, or a permission's holders; and until when.
6758
+ * A child as written. On a top-level org thread's plan, `project` names one of its owner's
6759
+ * projects to start it in, as that project's thread; left out, it's in its parent's scope.
6813
6760
  */
6814
- const StatementView = Schema.Struct({
6815
- approval: ApprovalId,
6816
- statement: WorkflowText,
6817
- hash: StatementHash,
6818
- asker: StatementAsker,
6819
- home: Schema.String,
6820
- from: Schema.Union([Schema.Literals(["accountable", "person"]), Schema.Struct({ permission: RepoPermissionName })]),
6821
- askedAt: Timestamp,
6822
- expiresAt: Timestamp
6761
+ const PlanChildInput = Schema.Struct({
6762
+ entry: PlanEntryKey,
6763
+ title: Title,
6764
+ outcome: Outcome,
6765
+ stackedOn: Schema.optionalKey(PlanEntryKey),
6766
+ required: Schema.Boolean,
6767
+ project: Schema.optionalKey(ProjectName)
6823
6768
  });
6824
6769
  /**
6825
- * The person's own requests, waiting for them; the passkey requests workflows' runs and gates asked
6826
- * them; and each machine's request this browser session is the answerer of, so a reload finds it
6827
- * again without its code.
6770
+ * A plan as written: `PlanRecord`'s shape, with its words in place of text IDs. Its repos are up
6771
+ * to 20 of the project's, by name; a parent whose work is all in its children names none.
6828
6772
  */
6829
- const ApprovalsWaiting = Schema.Struct({
6830
- approvals: Schema.Array(ApprovalView),
6831
- statements: Schema.Array(StatementView),
6832
- requests: Schema.Array(AttachmentRequestView)
6833
- });
6834
- /** Finding a terminal's request by the code it showed. */
6835
- const FindApproval = Schema.Union([Schema.Struct({ code: TypedApprovalCode }), Schema.Struct({ link: Schema.String.check(Schema.isPattern(/^[A-Za-z0-9_-]{22}$/)) })]);
6773
+ const PlanInput = Schema.Struct({
6774
+ approach: Schema.optionalKey(Approach),
6775
+ repos: Schema.Array(RepoName).check(Schema.isMaxLength(20)),
6776
+ outcomes: Schema.Array(PlanText).check(Schema.isBetweenLength(1, 50)),
6777
+ children: Schema.Array(PlanChildInput).check(Schema.isMaxLength(50))
6778
+ }).check(Schema.makeFilter((plan) => planProblem(plan) ?? true));
6836
6779
  /**
6837
- * Asking for the passkey's options to answer a request: a terminal's request needs its code
6838
- * again, so its ID alone, which travels where its code doesn't, never answers it. A machine's
6839
- * request needs its answerer, and the agent it chooses to connect, before a yes has options.
6780
+ * A project a plan's child names, as the Worker found it in the plan's owner before it asked the
6781
+ * scope: its ID, and whether the writer can see it; or, with no `project`, that the owner has
6782
+ * none by that name. The Worker refuses nothing for it, since only the scope knows which entries
6783
+ * a version keeps.
6840
6784
  */
6841
- const ApprovalOptionsRequest = Schema.Struct({
6842
- code: Schema.optionalKey(TypedApprovalCode),
6843
- folders: Schema.optionalKey(ConnectFolderChoices)
6785
+ const NamedProject = Schema.Struct({
6786
+ name: ProjectName,
6787
+ project: Schema.optionalKey(ProjectId),
6788
+ seen: Schema.Boolean
6844
6789
  });
6845
- /**
6846
- * An answer: yes, with the passkey, or no. For an agent's ask, each answer can set the owner's
6847
- * permission its own way, so a pairing that can't happen can't be sent: yes, and let agents do
6848
- * it without asking; no, and stop agents asking, with the passkey too.
6849
- */
6850
- const AnswerApproval = Schema.Union([
6851
- Schema.Struct({
6852
- answer: Schema.Literal("yes"),
6853
- permission: Schema.optionalKey(Schema.Literal("allow")),
6854
- assertion: PasskeyAsserted,
6855
- code: Schema.optionalKey(TypedApprovalCode),
6856
- /** The preview the page drew, where the command's endpoint names one. */
6857
- preview: Schema.optionalKey(PreviewEcho),
6858
- /** The statement the page showed, by its hash, for a workflow run's or a gate's passkey request. */
6859
- statement: Schema.optionalKey(Schema.Struct({ hash: StatementHash }))
6860
- }),
6861
- Schema.Struct({
6862
- answer: Schema.Literal("no"),
6863
- permission: Schema.Literal("deny"),
6864
- assertion: PasskeyAsserted,
6865
- code: Schema.optionalKey(TypedApprovalCode)
6866
- }),
6867
- Schema.Struct({
6868
- answer: Schema.Literal("no"),
6869
- code: Schema.optionalKey(TypedApprovalCode)
6870
- })
6871
- ]);
6872
- /**
6873
- * What running an approved command came to: its status, and its endpoint's answer or refusal,
6874
- * as JSON. ref0 reads it by the command's endpoint before anyone sees it, and whoever asked reads
6875
- * it the same way, with `ranOf`: this schema is part of the API the endpoints' types come from,
6876
- * so it can't be typed by them itself.
6877
- */
6878
- const Ran = Schema.Struct({
6879
- status: Schema.Int,
6880
- result: Schema.Json
6881
- });
6882
- /**
6883
- * A ran command whose answer its endpoint's schemas no longer read, such as one kept from
6884
- * before they changed: it ran, and what it came to can't be said.
6885
- */
6886
- var ResultUnreadable = class extends Schema.TaggedError()("ResultUnreadable", { nextStep: Schema.String }, { httpApiStatus: 500 }) {};
6887
- /**
6888
- * Where a request stands, for whoever asked: still waiting; approved, and running as soon as
6889
- * ref0 can; refused; expired; or what it ran to, with the endpoint it ran, whose schemas read it.
6890
- */
6891
- const ApprovalState = Schema.Union([
6892
- Schema.Struct({
6893
- state: Schema.Literal("waiting"),
6894
- expiresAt: Timestamp
6895
- }),
6896
- Schema.Struct({
6897
- state: Schema.Literal("approved"),
6898
- /** A machine's answering browser may forget its saved answer once the terminal kept it. */
6899
- collected: Schema.optionalKey(Schema.Literal(true))
6900
- }),
6901
- Schema.Struct({ state: Schema.Literal("refused") }),
6902
- Schema.Struct({ state: Schema.Literal("expired") }),
6903
- Schema.Struct({
6904
- state: Schema.Literal("ran"),
6905
- endpoint: CommandEndpoint,
6906
- ...Ran.fields
6907
- })
6908
- ]);
6909
- /** Only the answering browser receives a machine's answer code; state reads never carry it. */
6910
- const ApprovalAnswer = Schema.Union([
6911
- Schema.Struct({
6912
- ...ApprovalState.members[1].fields,
6913
- answerCode: ApprovalCode
6914
- }),
6915
- Schema.Struct({
6916
- ...ApprovalState.members[4].fields,
6917
- answerCode: ApprovalCode
6918
- }),
6919
- ApprovalState
6920
- ]);
6921
- /** A request that isn't there, or isn't the caller's to see, answers the same. */
6922
- var ApprovalNotFound = class extends Schema.TaggedError()("ApprovalNotFound", { nextStep: Schema.String }, { httpApiStatus: 404 }) {};
6923
- /** Too many asked, or too many codes typed, from here in the last hour. */
6924
- var SlowDown = class extends Schema.TaggedError()("SlowDown", { nextStep: Schema.String }, { httpApiStatus: 429 }) {};
6925
- /** A command's hash: the SHA-256 of its canonical JSON, as lowercase hex. */
6926
- const CommandHash = Schema.String.check(Schema.isPattern(/^[0-9a-f]{64}$/));
6927
6790
  Schema.Struct({
6928
- approval: ApprovalId,
6929
- endpoint: CommandEndpoint,
6930
- /** The command's canonical JSON. */
6931
- command: Schema.String,
6932
- commandHash: CommandHash,
6933
- asker: ApprovalAsker,
6934
- /** The browser session the person answered in, for their log. */
6935
- credential: CredentialRef,
6936
- generation: Generation,
6937
- /** When the request expires, in epoch milliseconds. */
6938
- deadline: Schema.Int,
6939
- challenge: Schema.String,
6940
- /** A machine answer retries only from its answering session, with this key. */
6941
- answerKey: Schema.optionalKey(RequestId),
6942
- /** An agent's ask: the credential it asked with, checked again before the answer counts. */
6943
- asking: Schema.optionalKey(TokenId),
6944
- /** The kind of action the person, answering, let agents take without asking from now on. */
6945
- allow: Schema.optionalKey(ScopeAction)
6791
+ ...commandFields,
6792
+ project: ProjectId,
6793
+ ownerName: OwnerName,
6794
+ number: ThreadNumber,
6795
+ plan: PlanInput,
6796
+ projects: Schema.Array(NamedProject).check(Schema.isMaxLength(50)),
6797
+ reason: Schema.optionalKey(Reason),
6798
+ propose: Schema.Boolean
6946
6799
  });
6947
6800
  Schema.Struct({
6948
- approval: ApprovalId,
6949
- action: ScopeAction,
6950
- credential: CredentialRef,
6951
- generation: Generation
6801
+ ...commandFields,
6802
+ ownerName: OwnerName,
6803
+ project: ProjectId,
6804
+ number: ThreadNumber,
6805
+ version: PlanVersion
6806
+ });
6807
+ /** A decision as it stands: what it asks, who decides, the answers so far, and how it closed. */
6808
+ const DecisionView = Schema.Struct({
6809
+ decision: DecisionId,
6810
+ kind: DecisionKind,
6811
+ subject: DecisionSubject,
6812
+ question: Schema.optionalKey(TextId),
6813
+ options: Schema.Array(AnswerOption),
6814
+ lean: Schema.optionalKey(AnswerKey),
6815
+ deciders: Schema.Array(Decider),
6816
+ deadline: Schema.optionalKey(Schema.DateTimeUtcFromMillis),
6817
+ state: Schema.Literals(["open", "closed"]),
6818
+ answers: Schema.Array(Schema.Struct({
6819
+ decider: DeciderRef,
6820
+ answer: AnswerKey,
6821
+ reason: Schema.optionalKey(TextId),
6822
+ at: Schema.DateTimeUtcFromMillis
6823
+ })),
6824
+ result: Schema.optionalKey(AnswerKey),
6825
+ closedWhy: Schema.optionalKey(DecisionClosedWhy)
6952
6826
  });
6953
- Schema.Union([Schema.Struct({ denied: Schema.Literal(true) }), Schema.Struct({
6954
- denied: Schema.Literal(false),
6955
- reason: Schema.Literals(["generation moved", "not set up"])
6956
- })]);
6957
- Schema.Union([Schema.Struct({
6958
- admitted: Schema.Literal(true),
6959
- admittedAt: Schema.Int,
6960
- answerCode: Schema.optionalKey(ApprovalCode)
6961
- }), Schema.Struct({
6962
- admitted: Schema.Literal(false),
6963
- reason: Schema.Literals([
6964
- "expired",
6965
- "generation moved",
6966
- "challenge used",
6967
- "not set up",
6968
- "asker gone"
6969
- ])
6970
- })]);
6971
- Schema.Union([Schema.Struct({ found: Schema.Literal(false) }), Schema.Struct({
6972
- found: Schema.Literal(true),
6973
- endpoint: CommandEndpoint,
6974
- command: Schema.String,
6975
- commandHash: CommandHash,
6976
- generation: Generation,
6977
- generationNow: Generation,
6978
- /** The `approval.answered` its admission recorded in the person's log. */
6979
- answeredEvent: EventId,
6980
- /** Who asked, as that `approval.answered` says. */
6981
- asker: ApprovalAsker,
6982
- /** What running it came to, once it ran, as `KeptRan` keeps it. */
6983
- result: Schema.optionalKey(Schema.String)
6984
- })]);
6985
6827
  /**
6986
- * A `Ran` as it's kept, on its receipt and on its approval: its JSON, as text. The Owner DO
6987
- * keeps it as it comes and runs no endpoint's schemas, so it's read only by its endpoint, with
6988
- * `keptRan`, which refuses one they don't take.
6828
+ * Where a decision is, as people write it: its thread, `pip/default#3`, or an org thread's,
6829
+ * `acme#7`; and a gate's pull request, `pip/notes#1`. Its Project DO writes them as it answers,
6830
+ * from its own threads and repos and the owner's name the Worker gives it, so each reads as it's
6831
+ * called now.
6989
6832
  */
6990
- const KeptRan = Schema.fromJsonString(Ran);
6991
- Schema.Struct({
6992
- approval: ApprovalId,
6993
- result: Schema.String
6833
+ const DecisionNames = Schema.Struct({
6834
+ thread: WrittenThread,
6835
+ pullRequest: Schema.optionalKey(WrittenPullRequest)
6994
6836
  });
6995
- Schema.Struct({ result: Schema.String });
6996
- Schema.Struct({ approval: ApprovalId });
6997
- Schema.Struct({ approval: ApprovalId });
6998
- Schema.Union([Schema.Struct({ expired: Schema.Literal(true) }), Schema.Struct({ expired: Schema.Literal(false) })]);
6999
- Schema.Struct({
7000
- person: PersonId,
7001
- approval: ApprovalId
6837
+ /** A decision as `/v1` answers it: as it stands, and where it is, in words. */
6838
+ const NamedDecision = Schema.Struct({
6839
+ ...DecisionView.fields,
6840
+ names: DecisionNames
7002
6841
  });
7003
- //#endregion
7004
- //#region ../contracts/src/browser-sessions.ts
7005
6842
  /**
7006
- * A browser session's cookie: Better Auth's, named for ref0, `Secure` and host-only on the
7007
- * app's one origin. It holds the session's token, signed; the directory and the person's Owner DO hold
7008
- * only the token's SHA-256.
6843
+ * A version as it stands: its content, its state, and, once proposed, where its approval is: its
6844
+ * repos still being checked, or who accepted it.
7009
6845
  */
7010
- const browserSessionCookie = "__Secure-ref0.session_token";
7011
- Schema.Struct({
7012
- token: TokenId,
7013
- secretHash: SecretHash,
7014
- expiresAt: Schema.DateTimeUtcFromMillis
6846
+ const PlanVersionView = Schema.Struct({
6847
+ thread: ThreadId,
6848
+ number: ThreadNumber,
6849
+ version: PlanVersion,
6850
+ state: PlanState,
6851
+ contentHash: PlanContentHash,
6852
+ plan: PlanView,
6853
+ /** Why it was written, when its writer said: never part of its content. */
6854
+ reason: Schema.optionalKey(TextId),
6855
+ writtenBy: ActorRef,
6856
+ writtenAt: Schema.DateTimeUtcFromMillis,
6857
+ /** Proposed, and its repos haven't all answered yet: it isn't accepted yet. */
6858
+ checking: Schema.Boolean,
6859
+ /**
6860
+ * What its check found when it ended, every repo answered or their time run out: the repos
6861
+ * that never answered. None for a draft, while it's being checked, or when a newer version or
6862
+ * the thread closing ended the check first.
6863
+ */
6864
+ check: Schema.optionalKey(PlanCheck),
6865
+ /**
6866
+ * Accepted, and waiting to be recorded: on its new `children` registering, or on a merge under
6867
+ * the thread's current revision `publishing`.
6868
+ */
6869
+ accepting: Schema.optionalKey(Schema.Literals(["children", "publishing"])),
6870
+ acceptedBy: Schema.optionalKey(PlanAcceptedBy)
7015
6871
  });
7016
- Schema.Struct({ registered: Schema.Boolean });
7017
- /**
7018
- * Where a signed-in person stands: naming, when the name their GitHub login gave was taken or
7019
- * kept back, with that login as a suggestion; setting up, once their owner is made and before
7020
- * it's ready with this session registered, which naming again finishes; or ready.
7021
- */
7022
- const Welcome = Schema.Union([Schema.Struct({
7023
- state: Schema.Literals(["ready", "setting up"]),
7024
- person: PersonId,
7025
- owner: OwnerId,
7026
- name: OwnerName
7027
- }), Schema.Struct({
7028
- state: Schema.Literal("naming"),
7029
- person: PersonId,
7030
- suggestion: Schema.optionalKey(Schema.String)
7031
- })]);
6872
+ /** A plan's free text refused before anything was stored, saying which. */
6873
+ var PlanTextRefused = class extends Schema.TaggedError()("PlanTextRefused", {
6874
+ /** Where it is in the plan, such as `outcomes[3]`. */
6875
+ where: Schema.String,
6876
+ problem: Schema.Literals([
6877
+ "empty",
6878
+ "too long",
6879
+ "looks like a secret"
6880
+ ]),
6881
+ nextStep: Schema.String
6882
+ }, { httpApiStatus: 422 }) {};
6883
+ /** A plan names a repo this project doesn't hold. */
6884
+ var PlanRepoNotFound = class extends Schema.TaggedError()("PlanRepoNotFound", {
6885
+ repo: RepoName,
6886
+ nextStep: Schema.String
6887
+ }, { httpApiStatus: 422 }) {};
7032
6888
  /**
7033
- * A browser session renewed: its expiry, in its person's Owner DO and in the directory, and on its cookie.
7034
- * Renewed at most once a day, so one asked sooner answers its expiry unchanged.
6889
+ * A plan names its scope's own repo, made just now, before the scope holds it. Nothing was kept
6890
+ * under the request's delivery ID, so the same request is written once it does.
7035
6891
  */
7036
- const SessionRenewed = Schema.Struct({ expiresAt: Schema.DateTimeUtcFromMillis });
6892
+ var OwnRepoNotReady = class extends Schema.TaggedError()("OwnRepoNotReady", {
6893
+ repo: RepoName,
6894
+ nextStep: Schema.String
6895
+ }, { httpApiStatus: 503 }) {};
7037
6896
  /**
7038
- * What starting a GitHub sign-in at `POST /auth/sign-in/social` answers: GitHub's address to go
7039
- * to, with the sign-in's state in a cookie set alongside. Better Auth's answer, which ref0 serves.
6897
+ * A child entry this plan can't have: a project named on a project thread's plan or a nested org
6898
+ * thread's, a project that isn't there or the writer can't see, one where its proposer may not
6899
+ * start threads, an entry moved to another project, or a stack across scopes. A project that
6900
+ * isn't there and one the writer can't see are refused alike, as `ProjectNotFound` is.
7040
6901
  */
7041
- const NavigationUrl = Schema.String.check(Schema.isPattern(/^https?:\/\/[^\s/]+(?:\/[^\s]*)?$/), Schema.makeFilter((url) => URL.canParse(url) || "an HTTP(S) URL the platform can parse")).pipe(Schema.brand("NavigationUrl"));
7042
- const SignInStarted = Schema.Struct({ url: NavigationUrl });
6902
+ var PlanChildRefused = class extends Schema.TaggedError()("PlanChildRefused", { nextStep: Schema.String }, { httpApiStatus: 422 }) {};
6903
+ var PlanVersionNotFound = class extends Schema.TaggedError()("PlanVersionNotFound", {
6904
+ number: ThreadNumber,
6905
+ version: PlanVersion,
6906
+ nextStep: Schema.String
6907
+ }, { httpApiStatus: 404 }) {};
7043
6908
  /**
7044
- * Where signing in sends the browser back to, as `POST /auth/sign-in/social` takes it: a path on
7045
- * ref0's own origin, such as `/welcome`, or a whole URL. Never a socket's.
7046
- */
7047
- const SignInDestination = Schema.String.pipe(Schema.brand("SignInDestination"));
7048
- /**
7049
- * What `POST /auth/sign-in/social` takes to start signing in with GitHub: where the browser
7050
- * lands coming back, and where it lands when signing in failed. Better Auth's body, which ref0
7051
- * serves.
7052
- */
7053
- const StartSignIn = Schema.Struct({
7054
- provider: Schema.Literal("github"),
7055
- callbackURL: SignInDestination,
7056
- errorCallbackURL: SignInDestination
7057
- });
7058
- /** The name a naming person chose for their owner. */
7059
- const NameOwner = Schema.Struct({ name: OwnerName });
7060
- //#endregion
7061
- //#region ../contracts/src/plan-events.ts
7062
- /**
7063
- * A plan's events, in its thread's project's log, written by the Project DO. A version's
7064
- * state is folded from these and its thread's, never recorded on its own.
7065
- */
7066
- /**
7067
- * A new version, as a draft. Its content never changes after this. Its reason, when given, says
7068
- * why it was written; it's beside the plan, never in it, so it's no part of the content hash.
7069
- */
7070
- const PlanWritten = eventKind("plan.written", Schema.Struct({
7071
- thread: ThreadId,
7072
- version: PlanVersion,
7073
- plan: PlanRecord,
7074
- contentHash: PlanContentHash,
7075
- reason: Schema.optionalKey(TextId)
7076
- }));
7077
- /**
7078
- * The version is put to approval: each repo it names is asked to answer for it, and ref0 says yes
7079
- * once they have or their time runs out. Any earlier draft or proposed version is
7080
- * superseded.
7081
- */
7082
- const PlanProposed = eventKind("plan.proposed", Schema.Struct({
7083
- thread: ThreadId,
7084
- version: PlanVersion
7085
- }));
7086
- /** Who said yes: ref0's plans part, once every repo answered or its time ran out. */
7087
- const PlanAcceptedBy = Schema.Struct({
7088
- kind: Schema.Literal("answered"),
7089
- part: Ref0Part
7090
- });
7091
- /** A child thread, by the plan entry that made it. */
7092
- const PlanChildThread = Schema.Struct({
7093
- entry: PlanEntryKey,
7094
- thread: ThreadId
7095
- });
7096
- const ChildThreads = Schema.Array(PlanChildThread).check(Schema.isMaxLength(50));
7097
- /**
7098
- * The version is accepted, and the one accepted before it superseded. Each child is named only
7099
- * once its owner has registered it.
7100
- */
7101
- const PlanAccepted = eventKind("plan.accepted", Schema.Struct({
7102
- thread: ThreadId,
7103
- version: PlanVersion,
7104
- by: PlanAcceptedBy,
7105
- /** The version accepted before this one. */
7106
- supersedes: Schema.optionalKey(PlanVersion),
7107
- /** Children made now, each starting in planning. */
7108
- started: ChildThreads,
7109
- /** Entries an earlier version dropped and this one adds back: the same child, reopened if it was closed. */
7110
- rejoined: ChildThreads,
7111
- /** Children no longer part of the plan, so it stops waiting on them. Closing one is its own event. */
7112
- dropped: ChildThreads
7113
- }));
7114
- //#endregion
7115
- //#region ../contracts/src/plan-calls.ts
7116
- /**
7117
- * Plans and decisions: what the Worker asks a thread's Project DO, once `Authorize` has said
7118
- * who's acting. A plan is written with its words; the Project DO stores each as a text and
7119
- * records the version with their IDs.
7120
- */
7121
- /** A plan's free text: one of its outcomes, or an answer's reason. */
7122
- const PlanText = Schema.String.check(Schema.isBetweenLength(1, 2048));
7123
- /**
7124
- * How a plan does its work, in system terms, for people: about a page. A revision replaces it,
7125
- * with a reason; no one appends to it.
7126
- */
7127
- const Approach = Schema.String.check(Schema.isBetweenLength(1, 6e3));
7128
- /**
7129
- * A child as written. On a top-level org thread's plan, `project` names one of its owner's
7130
- * projects to start it in, as that project's thread; left out, it's in its parent's scope.
7131
- */
7132
- const PlanChildInput = Schema.Struct({
7133
- entry: PlanEntryKey,
7134
- title: Title,
7135
- outcome: Outcome,
7136
- stackedOn: Schema.optionalKey(PlanEntryKey),
7137
- required: Schema.Boolean,
7138
- project: Schema.optionalKey(ProjectName)
7139
- });
7140
- /**
7141
- * A plan as written: `PlanRecord`'s shape, with its words in place of text IDs. Its repos are up
7142
- * to 20 of the project's, by name; a parent whose work is all in its children names none.
7143
- */
7144
- const PlanInput = Schema.Struct({
7145
- approach: Schema.optionalKey(Approach),
7146
- repos: Schema.Array(RepoName).check(Schema.isMaxLength(20)),
7147
- outcomes: Schema.Array(PlanText).check(Schema.isBetweenLength(1, 50)),
7148
- children: Schema.Array(PlanChildInput).check(Schema.isMaxLength(50))
7149
- }).check(Schema.makeFilter((plan) => planProblem(plan) ?? true));
7150
- /**
7151
- * A project a plan's child names, as the Worker found it in the plan's owner before it asked the
7152
- * scope: its ID, and whether the writer can see it; or, with no `project`, that the owner has
7153
- * none by that name. The Worker refuses nothing for it, since only the scope knows which entries
7154
- * a version keeps.
7155
- */
7156
- const NamedProject = Schema.Struct({
7157
- name: ProjectName,
7158
- project: Schema.optionalKey(ProjectId),
7159
- seen: Schema.Boolean
7160
- });
7161
- Schema.Struct({
7162
- ...commandFields,
7163
- project: ProjectId,
7164
- ownerName: OwnerName,
7165
- number: ThreadNumber,
7166
- plan: PlanInput,
7167
- projects: Schema.Array(NamedProject).check(Schema.isMaxLength(50)),
7168
- reason: Schema.optionalKey(Reason),
7169
- propose: Schema.Boolean
7170
- });
7171
- Schema.Struct({
7172
- ...commandFields,
7173
- ownerName: OwnerName,
7174
- project: ProjectId,
7175
- number: ThreadNumber,
7176
- version: PlanVersion
7177
- });
7178
- /** A decision as it stands: what it asks, who decides, the answers so far, and how it closed. */
7179
- const DecisionView = Schema.Struct({
7180
- decision: DecisionId,
7181
- kind: DecisionKind,
7182
- subject: DecisionSubject,
7183
- question: Schema.optionalKey(TextId),
7184
- options: Schema.Array(AnswerOption),
7185
- lean: Schema.optionalKey(AnswerKey),
7186
- deciders: Schema.Array(Decider),
7187
- deadline: Schema.optionalKey(Schema.DateTimeUtcFromMillis),
7188
- state: Schema.Literals(["open", "closed"]),
7189
- answers: Schema.Array(Schema.Struct({
7190
- decider: DeciderRef,
7191
- answer: AnswerKey,
7192
- reason: Schema.optionalKey(TextId),
7193
- at: Schema.DateTimeUtcFromMillis
7194
- })),
7195
- result: Schema.optionalKey(AnswerKey),
7196
- closedWhy: Schema.optionalKey(DecisionClosedWhy)
7197
- });
7198
- /**
7199
- * A version as it stands: its content, its state, and, once proposed, where its approval is: its
7200
- * repos still being checked, or who accepted it.
7201
- */
7202
- const PlanVersionView = Schema.Struct({
7203
- thread: ThreadId,
7204
- number: ThreadNumber,
7205
- version: PlanVersion,
7206
- state: PlanState,
7207
- contentHash: PlanContentHash,
7208
- plan: PlanView,
7209
- /** Why it was written, when its writer said: never part of its content. */
7210
- reason: Schema.optionalKey(TextId),
7211
- writtenBy: ActorRef,
7212
- writtenAt: Schema.DateTimeUtcFromMillis,
7213
- /** Proposed, and its repos haven't all answered yet: it isn't accepted yet. */
7214
- checking: Schema.Boolean,
7215
- /**
7216
- * What its check found when it ended, every repo answered or their time run out: the repos
7217
- * that never answered. None for a draft, while it's being checked, or when a newer version or
7218
- * the thread closing ended the check first.
7219
- */
7220
- check: Schema.optionalKey(PlanCheck),
7221
- /**
7222
- * Accepted, and waiting to be recorded: on its new `children` registering, or on a merge under
7223
- * the thread's current revision `publishing`.
7224
- */
7225
- accepting: Schema.optionalKey(Schema.Literals(["children", "publishing"])),
7226
- acceptedBy: Schema.optionalKey(PlanAcceptedBy)
7227
- });
7228
- /** A plan's free text refused before anything was stored, saying which. */
7229
- var PlanTextRefused = class extends Schema.TaggedError()("PlanTextRefused", {
7230
- /** Where it is in the plan, such as `outcomes[3]`. */
7231
- where: Schema.String,
7232
- problem: Schema.Literals([
7233
- "empty",
7234
- "too long",
7235
- "looks like a secret"
7236
- ]),
7237
- nextStep: Schema.String
7238
- }, { httpApiStatus: 422 }) {};
7239
- /** A plan names a repo this project doesn't hold. */
7240
- var PlanRepoNotFound = class extends Schema.TaggedError()("PlanRepoNotFound", {
7241
- repo: RepoName,
7242
- nextStep: Schema.String
7243
- }, { httpApiStatus: 422 }) {};
7244
- /**
7245
- * A plan names its scope's own repo, made just now, before the scope holds it. Nothing was kept
7246
- * under the request's delivery ID, so the same request is written once it does.
7247
- */
7248
- var OwnRepoNotReady = class extends Schema.TaggedError()("OwnRepoNotReady", {
7249
- repo: RepoName,
7250
- nextStep: Schema.String
7251
- }, { httpApiStatus: 503 }) {};
7252
- /**
7253
- * A child entry this plan can't have: a project named on a project thread's plan or a nested org
7254
- * thread's, a project that isn't there or the writer can't see, one where its proposer may not
7255
- * start threads, an entry moved to another project, or a stack across scopes. A project that
7256
- * isn't there and one the writer can't see are refused alike, as `ProjectNotFound` is.
7257
- */
7258
- var PlanChildRefused = class extends Schema.TaggedError()("PlanChildRefused", { nextStep: Schema.String }, { httpApiStatus: 422 }) {};
7259
- var PlanVersionNotFound = class extends Schema.TaggedError()("PlanVersionNotFound", {
7260
- number: ThreadNumber,
7261
- version: PlanVersion,
7262
- nextStep: Schema.String
7263
- }, { httpApiStatus: 404 }) {};
7264
- /**
7265
- * Only a thread's newest version is proposed, and never one already declined, nor one whose
7266
- * required or stacked children would close a cycle of threads waiting on each other.
6909
+ * Only a thread's newest version is proposed, and never one already declined, nor one whose
6910
+ * required or stacked children would close a cycle of threads waiting on each other.
7267
6911
  */
7268
6912
  var PlanNotProposable = class extends Schema.TaggedError()("PlanNotProposable", {
7269
6913
  version: PlanVersion,
@@ -7334,126 +6978,525 @@ var AnswerRefused = class extends Schema.TaggedError()("AnswerRefused", {
7334
6978
  nextStep: Schema.String
7335
6979
  }, { httpApiStatus: 409 }) {};
7336
6980
  /**
7337
- * Where a gate's answer goes: its Repo DO, which alone decides it, named by the gate's subject,
7338
- * with the answer's reason already stored here, where the fold reads it, and the scope it's in,
7339
- * which the Worker's `nextStep`s write. The Project DO keeps no receipt for a route, so a
7340
- * retry routes again, and the Repo DO keeps the answer's. The Worker takes it and never answers
7341
- * with it.
6981
+ * Where a gate's answer goes: its Repo DO, which alone decides it, named by the gate's subject,
6982
+ * with the answer's reason already stored here, where the fold reads it, and the scope it's in,
6983
+ * which the Worker's `nextStep`s write. The Project DO keeps no receipt for a route, so a
6984
+ * retry routes again, and the Repo DO keeps the answer's. The Worker takes it and never answers
6985
+ * with it.
6986
+ */
6987
+ const GateAnswerRoute = Schema.TaggedStruct("GateAnswerRoute", {
6988
+ scope: ThreadScope,
6989
+ decision: DecisionId,
6990
+ repo: RepoId,
6991
+ pullRequest: PullRequestNumber,
6992
+ version: VersionNumber,
6993
+ reason: Schema.optionalKey(TextId)
6994
+ });
6995
+ Schema.Union([
6996
+ NamedDecision,
6997
+ GateAnswerRoute,
6998
+ DecisionNotFound,
6999
+ AnswerRefused,
7000
+ PlanTextRefused,
7001
+ ProjectNotReady,
7002
+ ProjectNotFound,
7003
+ DeliveryOutsideWindow
7004
+ ]);
7005
+ Schema.Struct({
7006
+ ...commandFields,
7007
+ project: ProjectId,
7008
+ decision: DecisionId,
7009
+ pullRequest: PullRequestNumber,
7010
+ version: VersionNumber,
7011
+ answer: AnswerKey,
7012
+ reason: Schema.optionalKey(TextId),
7013
+ admin: Schema.Boolean
7014
+ });
7015
+ Schema.Union([
7016
+ Schema.Struct({
7017
+ outcome: Schema.Literal("answered"),
7018
+ deciders: Schema.Array(DeciderRef)
7019
+ }),
7020
+ Schema.Struct({ outcome: Schema.Literals([
7021
+ "decision closed",
7022
+ "not a decider",
7023
+ "already answered",
7024
+ "not an answer"
7025
+ ]) }),
7026
+ DeliveryOutsideWindow
7027
+ ]);
7028
+ /** Someone decisions wait on: a person, or an agent, and the roles the Worker found they hold. */
7029
+ const Waiter = Schema.Struct({
7030
+ decider: DeciderRef,
7031
+ roles: Schema.Array(Schema.Struct({
7032
+ role: Schema.Literal("admin"),
7033
+ owner: OwnerId
7034
+ }))
7035
+ });
7036
+ Schema.Struct({
7037
+ ownerName: OwnerName,
7038
+ you: Waiter,
7039
+ person: Schema.optionalKey(Waiter),
7040
+ decisions: Schema.optionalKey(Schema.Array(DecisionId).check(Schema.isMaxLength(100)))
7041
+ });
7042
+ /**
7043
+ * An open decision that waits on the caller: on them, or, for an agent, on its person, who
7044
+ * answers it. A gate never takes an agent's answer, so an agent's gate always waits on its person.
7045
+ */
7046
+ const WaitingDecision = Schema.Struct({
7047
+ ...NamedDecision.fields,
7048
+ waitsOn: Schema.Literals(["you", "your person"])
7049
+ });
7050
+ const WaitingPage = Schema.Struct({ decisions: Schema.Array(WaitingDecision) });
7051
+ Schema.Struct({
7052
+ ownerName: OwnerName,
7053
+ number: ThreadNumber
7054
+ });
7055
+ const PlanVersionPage = Schema.Struct({ versions: Schema.Array(PlanVersionView) });
7056
+ Schema.Union([PlanVersionPage, ThreadNotFound]);
7057
+ /** One decision of the project's, by its ID. */
7058
+ /** At most how long a read of a decision waits for it to close, in seconds. */
7059
+ const DecisionWait = Schema.Int.check(Schema.isBetween({
7060
+ minimum: 1,
7061
+ maximum: 60
7062
+ }));
7063
+ Schema.Struct({
7064
+ ownerName: OwnerName,
7065
+ decision: DecisionId,
7066
+ wait: Schema.optionalKey(DecisionWait)
7067
+ });
7068
+ Schema.Union([NamedDecision, DecisionNotFound]);
7069
+ /**
7070
+ * `POST …/plans`: a version, as a draft unless it's proposed at once, and why it was written.
7071
+ * A first version needs no reason, and a later one may leave it out.
7072
+ */
7073
+ const WritePlanRequest = Schema.Struct({
7074
+ plan: PlanInput,
7075
+ reason: Schema.optionalKey(Reason),
7076
+ propose: Schema.optionalKey(Schema.Boolean)
7077
+ });
7078
+ /** `POST …/decisions/{decision}/answers`: one of its answers, and why, in words. */
7079
+ const AnswerRequest = Schema.Struct({
7080
+ answer: AnswerKey,
7081
+ reason: Schema.optionalKey(PlanText)
7082
+ });
7083
+ Schema.Struct({
7084
+ thread: ThreadId,
7085
+ owner: OwnerId,
7086
+ parent: ThreadId,
7087
+ parentScope: ProjectId,
7088
+ parentNumber: ThreadNumber,
7089
+ fromPlan: Schema.Struct({
7090
+ version: PlanVersion,
7091
+ entry: PlanEntryKey
7092
+ }),
7093
+ title: Title,
7094
+ outcome: Outcome,
7095
+ required: Schema.Boolean,
7096
+ actor: ActorRef,
7097
+ cause: EventRef
7098
+ });
7099
+ /** The child as it stands, whether this call started it or an earlier one did. */
7100
+ const RemoteChildStarted = Schema.Struct({
7101
+ number: ThreadNumber,
7102
+ stage: ThreadStage,
7103
+ stageRevision: StageRevision
7104
+ });
7105
+ Schema.Union([RemoteChildStarted, ProjectNotReady]);
7106
+ Schema.Struct({
7107
+ thread: ThreadId,
7108
+ number: ThreadNumber
7109
+ });
7110
+ /** A child's stage and its revision, as one read found them. */
7111
+ const ChildStageView = Schema.Struct({
7112
+ stage: ThreadStage,
7113
+ stageRevision: StageRevision
7114
+ });
7115
+ Schema.Union([ChildStageView, ThreadNotFound]);
7116
+ //#endregion
7117
+ //#region ../contracts/src/approvals.ts
7118
+ /**
7119
+ * Approvals, as their commands travel: what a terminal, a page or an agent asks its person to
7120
+ * say yes to. A command is one endpoint, by its name in the surface table, and its request, as
7121
+ * that endpoint encodes it. ref0 decodes each part by the endpoint's own `Schema` before it
7122
+ * keeps or shows anything, so a command is exactly what the endpoint would take.
7123
+ */
7124
+ /**
7125
+ * An endpoint by its name in the surface table, `group.endpoint`. Checked against the table
7126
+ * where the command is made canonical, since the table is typed from the API this is part of.
7127
+ */
7128
+ const CommandEndpoint = Schema.String.check(Schema.isPattern(/^[a-zA-Z]+\.[a-zA-Z]+$/));
7129
+ /** One command, as asked: its endpoint, and its request's path, query and payload, encoded. */
7130
+ const ApprovalCommand = Schema.Struct({
7131
+ endpoint: CommandEndpoint,
7132
+ params: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
7133
+ query: Schema.optionalKey(Schema.JsonObject),
7134
+ payload: Schema.optionalKey(Schema.Json)
7135
+ });
7136
+ /** A command ref0 won't ask anyone about: not an endpoint's request, or not one an approval runs. */
7137
+ var CommandRefused = class extends Schema.TaggedError()("CommandRefused", { nextStep: Schema.String }, { httpApiStatus: 400 }) {};
7138
+ /** A terminal asking: its command, and the hash of the secret it collects the result with. */
7139
+ const StartApproval = Schema.Struct({
7140
+ command: ApprovalCommand,
7141
+ secretHash: SecretHash
7142
+ });
7143
+ /** A terminal's approval, started: what to show its person, and until when. */
7144
+ const ApprovalStarted = Schema.Struct({
7145
+ approval: ApprovalId,
7146
+ code: ApprovalCode,
7147
+ expiresAt: Timestamp
7148
+ });
7149
+ /**
7150
+ * An answer that waits for the person: the call only the person may make, or that agents ask
7151
+ * for, became an approval, which the person answers on their own page. A `202`, never a failure
7152
+ * of the call: whoever asked collects the result with `GET /v1/approvals/{approval}`.
7153
+ */
7154
+ var ApprovalNeeded = class extends Schema.TaggedError()("ApprovalNeeded", {
7155
+ approval: ApprovalId,
7156
+ says: Schema.String,
7157
+ expiresAt: Timestamp
7158
+ }, { httpApiStatus: 202 }) {};
7159
+ const terminalAsker = Schema.Struct({ kind: Schema.Literal("terminal") });
7160
+ const pageAsker = Schema.Struct({ kind: Schema.Literal("page") });
7161
+ const agentAsker = Schema.Struct({
7162
+ kind: Schema.Literal("agent"),
7163
+ agent: AgentId,
7164
+ handle: AgentHandle,
7165
+ session: SessionId,
7166
+ /** The attachment its credential was made under, for a person's agent in an organization. */
7167
+ attachment: Schema.optionalKey(CredentialRef)
7168
+ });
7169
+ /**
7170
+ * Who asked, as a receipt keeps it: a terminal, one of ref0's pages, an agent, or a machine
7171
+ * asking to connect, by its ID and its own word for itself, and from where.
7172
+ */
7173
+ const ApprovalAsker = Schema.Union([
7174
+ terminalAsker,
7175
+ pageAsker,
7176
+ Schema.Struct({
7177
+ kind: Schema.Literal("machine"),
7178
+ machine: MachineId,
7179
+ machineName: MachineName,
7180
+ askedFrom: Schema.optionalKey(Schema.String)
7181
+ }),
7182
+ agentAsker
7183
+ ]);
7184
+ /**
7185
+ * Who asked, as the person reads a request before they answer: a terminal, a page or an agent.
7186
+ * A machine's request is read as an `AttachmentRequestView`, never as a command.
7187
+ */
7188
+ const ApprovalViewAsker = Schema.Union([
7189
+ terminalAsker,
7190
+ pageAsker,
7191
+ agentAsker
7192
+ ]);
7193
+ /** A request, as the person reads it before they answer. */
7194
+ const ApprovalView = Schema.Struct({
7195
+ approval: ApprovalId,
7196
+ /** The command as its CLI line. */
7197
+ says: Schema.String,
7198
+ /** Everything the command carries, each named, in order: nothing it would run is left out. */
7199
+ fields: Schema.Array(Schema.Struct({
7200
+ name: Schema.String,
7201
+ value: Schema.String,
7202
+ part: Schema.Literals([
7203
+ "path",
7204
+ "query",
7205
+ "payload"
7206
+ ])
7207
+ })),
7208
+ command: ApprovalCommand,
7209
+ asker: ApprovalViewAsker,
7210
+ askedFrom: Schema.optionalKey(Schema.String),
7211
+ askedAt: Timestamp,
7212
+ expiresAt: Timestamp,
7213
+ /**
7214
+ * An agent's ask: the kind of action its answer may set agents' permission for, and whether a
7215
+ * yes may let agents take it without asking. A no with the passkey may always stop them asking.
7216
+ */
7217
+ agentPermission: Schema.optionalKey(Schema.Struct({
7218
+ action: ScopeAction,
7219
+ mayAllow: Schema.Boolean
7220
+ })),
7221
+ /**
7222
+ * What the command acts on, drawn as ref0 resolves it for the person reading, where its
7223
+ * endpoint names a preview. Without one the page can draw, it offers only no.
7224
+ */
7225
+ preview: Schema.optionalKey(ApprovalPreview)
7226
+ });
7227
+ /** A statement's hash: the SHA-256 of its words and quoted values as JSON, as lowercase hex. */
7228
+ const StatementHash = Schema.String.check(Schema.isPattern(/^[0-9a-f]{64}$/));
7229
+ /**
7230
+ * Who asks for a yes to a statement: a workflow's run, `passkey.run`'s, by the workflow and the
7231
+ * run; or a gate's check, `passkey.check`'s, by the check, the pull request's version it gates and
7232
+ * the check run its statement is over.
7233
+ */
7234
+ const StatementAsker = Schema.Union([Schema.Struct({
7235
+ kind: Schema.Literal("workflow"),
7236
+ workflow: WorkflowName,
7237
+ run: WorkflowRunId
7238
+ }), Schema.Struct({
7239
+ kind: Schema.Literal("gate"),
7240
+ check: CheckKey,
7241
+ pullRequest: PullRequestNumber,
7242
+ version: VersionNumber,
7243
+ run: CheckRunId
7244
+ })]);
7245
+ /**
7246
+ * A passkey request, as the person reads it before they answer: the statement, its words as its
7247
+ * asker wrote them and each value it quotes, and its hash; who asks; where, a workflow's
7248
+ * `owner/repo` or a thread's `owner/project#number`, or a gate's pull request, `owner/repo#number`;
7249
+ * whom it asks, the run's accountable person, or for a gate the person behind its run, a person by
7250
+ * name, or a permission's holders; and until when.
7251
+ */
7252
+ const StatementView = Schema.Struct({
7253
+ approval: ApprovalId,
7254
+ statement: WorkflowText,
7255
+ hash: StatementHash,
7256
+ asker: StatementAsker,
7257
+ home: Schema.String,
7258
+ from: Schema.Union([Schema.Literals(["accountable", "person"]), Schema.Struct({ permission: RepoPermissionName })]),
7259
+ askedAt: Timestamp,
7260
+ expiresAt: Timestamp
7261
+ });
7262
+ /**
7263
+ * The person's own requests, waiting for them; the passkey requests workflows' runs and gates asked
7264
+ * them; each machine's request this browser session is the answerer of, so a reload finds it
7265
+ * again without its code; and the gates' decisions that wait on their yes or no, in every project
7266
+ * they see, oldest first.
7267
+ */
7268
+ const ApprovalsWaiting = Schema.Struct({
7269
+ approvals: Schema.Array(ApprovalView),
7270
+ statements: Schema.Array(StatementView),
7271
+ requests: Schema.Array(AttachmentRequestView),
7272
+ decisions: Schema.Array(NamedDecision)
7273
+ });
7274
+ /** Finding a terminal's request by the code it showed. */
7275
+ const FindApproval = Schema.Union([Schema.Struct({ code: TypedApprovalCode }), Schema.Struct({ link: Schema.String.check(Schema.isPattern(/^[A-Za-z0-9_-]{22}$/)) })]);
7276
+ /**
7277
+ * Asking for the passkey's options to answer a request: a terminal's request needs its code
7278
+ * again, so its ID alone, which travels where its code doesn't, never answers it. A machine's
7279
+ * request needs its answerer, and the agent it chooses to connect, before a yes has options.
7280
+ */
7281
+ const ApprovalOptionsRequest = Schema.Struct({
7282
+ code: Schema.optionalKey(TypedApprovalCode),
7283
+ folders: Schema.optionalKey(ConnectFolderChoices)
7284
+ });
7285
+ /**
7286
+ * An answer: yes, with the passkey, or no. For an agent's ask, each answer can set the owner's
7287
+ * permission its own way, so a pairing that can't happen can't be sent: yes, and let agents do
7288
+ * it without asking; no, and stop agents asking, with the passkey too.
7289
+ */
7290
+ const AnswerApproval = Schema.Union([
7291
+ Schema.Struct({
7292
+ answer: Schema.Literal("yes"),
7293
+ permission: Schema.optionalKey(Schema.Literal("allow")),
7294
+ assertion: PasskeyAsserted,
7295
+ code: Schema.optionalKey(TypedApprovalCode),
7296
+ /** The preview the page drew, where the command's endpoint names one. */
7297
+ preview: Schema.optionalKey(PreviewEcho),
7298
+ /** The statement the page showed, by its hash, for a workflow run's or a gate's passkey request. */
7299
+ statement: Schema.optionalKey(Schema.Struct({ hash: StatementHash }))
7300
+ }),
7301
+ Schema.Struct({
7302
+ answer: Schema.Literal("no"),
7303
+ permission: Schema.Literal("deny"),
7304
+ assertion: PasskeyAsserted,
7305
+ code: Schema.optionalKey(TypedApprovalCode)
7306
+ }),
7307
+ Schema.Struct({
7308
+ answer: Schema.Literal("no"),
7309
+ code: Schema.optionalKey(TypedApprovalCode)
7310
+ })
7311
+ ]);
7312
+ /**
7313
+ * What running an approved command came to: its status, and its endpoint's answer or refusal,
7314
+ * as JSON. ref0 reads it by the command's endpoint before anyone sees it, and whoever asked reads
7315
+ * it the same way, with `ranOf`: this schema is part of the API the endpoints' types come from,
7316
+ * so it can't be typed by them itself.
7317
+ */
7318
+ const Ran = Schema.Struct({
7319
+ status: Schema.Int,
7320
+ result: Schema.Json
7321
+ });
7322
+ /**
7323
+ * A ran command whose answer its endpoint's schemas no longer read, such as one kept from
7324
+ * before they changed: it ran, and what it came to can't be said.
7325
+ */
7326
+ var ResultUnreadable = class extends Schema.TaggedError()("ResultUnreadable", { nextStep: Schema.String }, { httpApiStatus: 500 }) {};
7327
+ /**
7328
+ * Where a request stands, for whoever asked: still waiting; approved, and running as soon as
7329
+ * ref0 can; refused; expired; or what it ran to, with the endpoint it ran, whose schemas read it.
7330
+ */
7331
+ const ApprovalState = Schema.Union([
7332
+ Schema.Struct({
7333
+ state: Schema.Literal("waiting"),
7334
+ expiresAt: Timestamp
7335
+ }),
7336
+ Schema.Struct({
7337
+ state: Schema.Literal("approved"),
7338
+ /** A machine's answering browser may forget its saved answer once the terminal kept it. */
7339
+ collected: Schema.optionalKey(Schema.Literal(true))
7340
+ }),
7341
+ Schema.Struct({ state: Schema.Literal("refused") }),
7342
+ Schema.Struct({ state: Schema.Literal("expired") }),
7343
+ Schema.Struct({
7344
+ state: Schema.Literal("ran"),
7345
+ endpoint: CommandEndpoint,
7346
+ ...Ran.fields
7347
+ })
7348
+ ]);
7349
+ /** Only the answering browser receives a machine's answer code; state reads never carry it. */
7350
+ const ApprovalAnswer = Schema.Union([
7351
+ Schema.Struct({
7352
+ ...ApprovalState.members[1].fields,
7353
+ answerCode: ApprovalCode
7354
+ }),
7355
+ Schema.Struct({
7356
+ ...ApprovalState.members[4].fields,
7357
+ answerCode: ApprovalCode
7358
+ }),
7359
+ ApprovalState
7360
+ ]);
7361
+ /** A request that isn't there, or isn't the caller's to see, answers the same. */
7362
+ var ApprovalNotFound = class extends Schema.TaggedError()("ApprovalNotFound", { nextStep: Schema.String }, { httpApiStatus: 404 }) {};
7363
+ /** Too many asked, or too many codes typed, from here in the last hour. */
7364
+ var SlowDown = class extends Schema.TaggedError()("SlowDown", { nextStep: Schema.String }, { httpApiStatus: 429 }) {};
7365
+ /** A command's hash: the SHA-256 of its canonical JSON, as lowercase hex. */
7366
+ const CommandHash = Schema.String.check(Schema.isPattern(/^[0-9a-f]{64}$/));
7367
+ Schema.Struct({
7368
+ approval: ApprovalId,
7369
+ endpoint: CommandEndpoint,
7370
+ /** The command's canonical JSON. */
7371
+ command: Schema.String,
7372
+ commandHash: CommandHash,
7373
+ asker: ApprovalAsker,
7374
+ /** The browser session the person answered in, for their log. */
7375
+ credential: CredentialRef,
7376
+ generation: Generation,
7377
+ /** When the request expires, in epoch milliseconds. */
7378
+ deadline: Schema.Int,
7379
+ challenge: Schema.String,
7380
+ /** A machine answer retries only from its answering session, with this key. */
7381
+ answerKey: Schema.optionalKey(RequestId),
7382
+ /** An agent's ask: the credential it asked with, checked again before the answer counts. */
7383
+ asking: Schema.optionalKey(TokenId),
7384
+ /** The kind of action the person, answering, let agents take without asking from now on. */
7385
+ allow: Schema.optionalKey(ScopeAction)
7386
+ });
7387
+ Schema.Struct({
7388
+ approval: ApprovalId,
7389
+ action: ScopeAction,
7390
+ credential: CredentialRef,
7391
+ generation: Generation
7392
+ });
7393
+ Schema.Union([Schema.Struct({ denied: Schema.Literal(true) }), Schema.Struct({
7394
+ denied: Schema.Literal(false),
7395
+ reason: Schema.Literals(["generation moved", "not set up"])
7396
+ })]);
7397
+ Schema.Union([Schema.Struct({
7398
+ admitted: Schema.Literal(true),
7399
+ admittedAt: Schema.Int,
7400
+ answerCode: Schema.optionalKey(ApprovalCode)
7401
+ }), Schema.Struct({
7402
+ admitted: Schema.Literal(false),
7403
+ reason: Schema.Literals([
7404
+ "expired",
7405
+ "generation moved",
7406
+ "challenge used",
7407
+ "not set up",
7408
+ "asker gone"
7409
+ ])
7410
+ })]);
7411
+ Schema.Union([Schema.Struct({ found: Schema.Literal(false) }), Schema.Struct({
7412
+ found: Schema.Literal(true),
7413
+ endpoint: CommandEndpoint,
7414
+ command: Schema.String,
7415
+ commandHash: CommandHash,
7416
+ generation: Generation,
7417
+ generationNow: Generation,
7418
+ /** The `approval.answered` its admission recorded in the person's log. */
7419
+ answeredEvent: EventId,
7420
+ /** Who asked, as that `approval.answered` says. */
7421
+ asker: ApprovalAsker,
7422
+ /** What running it came to, once it ran, as `KeptRan` keeps it. */
7423
+ result: Schema.optionalKey(Schema.String)
7424
+ })]);
7425
+ /**
7426
+ * A `Ran` as it's kept, on its receipt and on its approval: its JSON, as text. The Owner DO
7427
+ * keeps it as it comes and runs no endpoint's schemas, so it's read only by its endpoint, with
7428
+ * `keptRan`, which refuses one they don't take.
7342
7429
  */
7343
- const GateAnswerRoute = Schema.TaggedStruct("GateAnswerRoute", {
7344
- scope: ThreadScope,
7345
- decision: DecisionId,
7346
- repo: RepoId,
7347
- pullRequest: PullRequestNumber,
7348
- version: VersionNumber,
7349
- reason: Schema.optionalKey(TextId)
7350
- });
7351
- Schema.Union([
7352
- DecisionView,
7353
- GateAnswerRoute,
7354
- DecisionNotFound,
7355
- AnswerRefused,
7356
- PlanTextRefused,
7357
- ProjectNotReady,
7358
- ProjectNotFound,
7359
- DeliveryOutsideWindow
7360
- ]);
7361
- Schema.Struct({
7362
- ...commandFields,
7363
- project: ProjectId,
7364
- decision: DecisionId,
7365
- pullRequest: PullRequestNumber,
7366
- version: VersionNumber,
7367
- answer: AnswerKey,
7368
- reason: Schema.optionalKey(TextId),
7369
- admin: Schema.Boolean
7370
- });
7371
- Schema.Union([
7372
- Schema.Struct({
7373
- outcome: Schema.Literal("answered"),
7374
- deciders: Schema.Array(DeciderRef)
7375
- }),
7376
- Schema.Struct({ outcome: Schema.Literals([
7377
- "decision closed",
7378
- "not a decider",
7379
- "already answered",
7380
- "not an answer"
7381
- ]) }),
7382
- DeliveryOutsideWindow
7383
- ]);
7430
+ const KeptRan = Schema.fromJsonString(Ran);
7384
7431
  Schema.Struct({
7385
- decider: DeciderRef,
7386
- roles: Schema.optionalKey(Schema.Array(Schema.Struct({
7387
- role: Schema.Literal("admin"),
7388
- owner: OwnerId
7389
- })))
7432
+ approval: ApprovalId,
7433
+ result: Schema.String
7390
7434
  });
7391
- const WaitingPage = Schema.Struct({ decisions: Schema.Array(DecisionView) });
7435
+ Schema.Struct({ result: Schema.String });
7436
+ Schema.Struct({ approval: ApprovalId });
7437
+ Schema.Struct({ approval: ApprovalId });
7438
+ Schema.Union([Schema.Struct({ expired: Schema.Literal(true) }), Schema.Struct({ expired: Schema.Literal(false) })]);
7392
7439
  Schema.Struct({
7393
- ownerName: OwnerName,
7394
- number: ThreadNumber
7440
+ person: PersonId,
7441
+ approval: ApprovalId
7395
7442
  });
7396
- const PlanVersionPage = Schema.Struct({ versions: Schema.Array(PlanVersionView) });
7397
- Schema.Union([PlanVersionPage, ThreadNotFound]);
7398
- /** One decision of the project's, by its ID. */
7399
- /** At most how long a read of a decision waits for it to close, in seconds. */
7400
- const DecisionWait = Schema.Int.check(Schema.isBetween({
7401
- minimum: 1,
7402
- maximum: 60
7403
- }));
7443
+ //#endregion
7444
+ //#region ../contracts/src/browser-sessions.ts
7445
+ /**
7446
+ * A browser session's cookie: Better Auth's, named for ref0, `Secure` and host-only on the
7447
+ * app's one origin. It holds the session's token, signed; the directory and the person's Owner DO hold
7448
+ * only the token's SHA-256.
7449
+ */
7450
+ const browserSessionCookie = "__Secure-ref0.session_token";
7404
7451
  Schema.Struct({
7405
- ownerName: OwnerName,
7406
- decision: DecisionId,
7407
- wait: Schema.optionalKey(DecisionWait)
7452
+ token: TokenId,
7453
+ secretHash: SecretHash,
7454
+ expiresAt: Schema.DateTimeUtcFromMillis
7408
7455
  });
7409
- Schema.Union([DecisionView, DecisionNotFound]);
7456
+ Schema.Struct({ registered: Schema.Boolean });
7410
7457
  /**
7411
- * `POST …/plans`: a version, as a draft unless it's proposed at once, and why it was written.
7412
- * A first version needs no reason, and a later one may leave it out.
7458
+ * Where a signed-in person stands: naming, when the name their GitHub login gave was taken or
7459
+ * kept back, with that login as a suggestion; setting up, once their owner is made and before
7460
+ * it's ready with this session registered, which naming again finishes; or ready.
7413
7461
  */
7414
- const WritePlanRequest = Schema.Struct({
7415
- plan: PlanInput,
7416
- reason: Schema.optionalKey(Reason),
7417
- propose: Schema.optionalKey(Schema.Boolean)
7418
- });
7419
- /** `POST …/decisions/{decision}/answers`: one of its answers, and why, in words. */
7420
- const AnswerRequest = Schema.Struct({
7421
- answer: AnswerKey,
7422
- reason: Schema.optionalKey(PlanText)
7423
- });
7424
- Schema.Struct({
7425
- thread: ThreadId,
7462
+ const Welcome = Schema.Union([Schema.Struct({
7463
+ state: Schema.Literals(["ready", "setting up"]),
7464
+ person: PersonId,
7426
7465
  owner: OwnerId,
7427
- parent: ThreadId,
7428
- parentScope: ProjectId,
7429
- parentNumber: ThreadNumber,
7430
- fromPlan: Schema.Struct({
7431
- version: PlanVersion,
7432
- entry: PlanEntryKey
7433
- }),
7434
- title: Title,
7435
- outcome: Outcome,
7436
- required: Schema.Boolean,
7437
- actor: ActorRef,
7438
- cause: EventRef
7439
- });
7440
- /** The child as it stands, whether this call started it or an earlier one did. */
7441
- const RemoteChildStarted = Schema.Struct({
7442
- number: ThreadNumber,
7443
- stage: ThreadStage,
7444
- stageRevision: StageRevision
7445
- });
7446
- Schema.Union([RemoteChildStarted, ProjectNotReady]);
7447
- Schema.Struct({
7448
- thread: ThreadId,
7449
- number: ThreadNumber
7450
- });
7451
- /** A child's stage and its revision, as one read found them. */
7452
- const ChildStageView = Schema.Struct({
7453
- stage: ThreadStage,
7454
- stageRevision: StageRevision
7466
+ name: OwnerName
7467
+ }), Schema.Struct({
7468
+ state: Schema.Literal("naming"),
7469
+ person: PersonId,
7470
+ suggestion: Schema.optionalKey(Schema.String)
7471
+ })]);
7472
+ /**
7473
+ * A browser session renewed: its expiry, in its person's Owner DO and in the directory, and on its cookie.
7474
+ * Renewed at most once a day, so one asked sooner answers its expiry unchanged.
7475
+ */
7476
+ const SessionRenewed = Schema.Struct({ expiresAt: Schema.DateTimeUtcFromMillis });
7477
+ /**
7478
+ * What starting a GitHub sign-in at `POST /auth/sign-in/social` answers: GitHub's address to go
7479
+ * to, with the sign-in's state in a cookie set alongside. Better Auth's answer, which ref0 serves.
7480
+ */
7481
+ const NavigationUrl = Schema.String.check(Schema.isPattern(/^https?:\/\/[^\s/]+(?:\/[^\s]*)?$/), Schema.makeFilter((url) => URL.canParse(url) || "an HTTP(S) URL the platform can parse")).pipe(Schema.brand("NavigationUrl"));
7482
+ const SignInStarted = Schema.Struct({ url: NavigationUrl });
7483
+ /**
7484
+ * Where signing in sends the browser back to, as `POST /auth/sign-in/social` takes it: a path on
7485
+ * ref0's own origin, such as `/welcome`, or a whole URL. Never a socket's.
7486
+ */
7487
+ const SignInDestination = Schema.String.pipe(Schema.brand("SignInDestination"));
7488
+ /**
7489
+ * What `POST /auth/sign-in/social` takes to start signing in with GitHub: where the browser
7490
+ * lands coming back, and where it lands when signing in failed. Better Auth's body, which ref0
7491
+ * serves.
7492
+ */
7493
+ const StartSignIn = Schema.Struct({
7494
+ provider: Schema.Literal("github"),
7495
+ callbackURL: SignInDestination,
7496
+ errorCallbackURL: SignInDestination
7455
7497
  });
7456
- Schema.Union([ChildStageView, ThreadNotFound]);
7498
+ /** The name a naming person chose for their owner. */
7499
+ const NameOwner = Schema.Struct({ name: OwnerName });
7457
7500
  //#endregion
7458
7501
  //#region ../contracts/src/ask-calls.ts
7459
7502
  /**
@@ -10268,10 +10311,21 @@ signInRoute("POST", "/oauth2/consent");
10268
10311
  */
10269
10312
  const oauthDiscoveryPaths = ["/.well-known/oauth-protected-resource/mcp", `${oauthRoutes.metadata.pathUnderBase}${signInBase}`];
10270
10313
  /**
10314
+ * Where an outside service finds identity tokens' issuer and its public keys, each at the
10315
+ * origin's root: the issuer's discovery document, and its JWKS. Only these two: every other path
10316
+ * under `/.well-known/` is a page's.
10317
+ */
10318
+ const identityDiscoveryRoutes = {
10319
+ configuration: "/.well-known/openid-configuration",
10320
+ keys: "/.well-known/jwks.json"
10321
+ };
10322
+ /** Whether a path is one of identity tokens' two published documents. */
10323
+ const isIdentityDiscoveryPath = (pathname) => pathname === identityDiscoveryRoutes.configuration || pathname === identityDiscoveryRoutes.keys;
10324
+ /**
10271
10325
  * Whether a path is Worker "server"'s to answer, not a page's: the API's, signing in's, a
10272
- * connection's webhook, or OAuth's metadata.
10326
+ * connection's webhook, OAuth's metadata, or identity tokens' published documents.
10273
10327
  */
10274
- const isRef0Path = (pathname) => isSignInPath(pathname) || isHooksPath(pathname) || pathname === "/v1" || pathname.startsWith(`/v1/`) || oauthDiscoveryPaths.includes(pathname);
10328
+ const isRef0Path = (pathname) => isSignInPath(pathname) || isHooksPath(pathname) || pathname === "/v1" || pathname.startsWith(`/v1/`) || oauthDiscoveryPaths.includes(pathname) || isIdentityDiscoveryPath(pathname);
10275
10329
  //#endregion
10276
10330
  //#region ../contracts/src/live-calls.ts
10277
10331
  /**
@@ -11584,6 +11638,151 @@ Schema.Struct({
11584
11638
  name: RepoName
11585
11639
  }))
11586
11640
  });
11641
+ /** Where an agent's session asks for an identity token. */
11642
+ const identityTokenPath = "/v1/me/identity-token";
11643
+ /** One DNS label, or one part of an IPv4 address: letters, digits and inner hyphens. */
11644
+ const label = "[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?";
11645
+ /** `https://`, a host name, then maybe `:` and a port with no leading zero. */
11646
+ const audiencePattern = new RegExp(`^https://(${label}(?:\\.${label})*)(?::([1-9][0-9]{0,4}))?$`);
11647
+ /**
11648
+ * Whether the platform's URL parser reads the audience as written: an `https` URL whose host is
11649
+ * the audience's own, lowercased, and whose port is its own, or none for 443. That refuses a host
11650
+ * the parser rewrites, such as `https://1.2.3` read as `1.2.0.3`, and a port past 65535.
11651
+ */
11652
+ const parsesAsWritten = (audience) => {
11653
+ const parts = audiencePattern.exec(audience);
11654
+ if (parts === null || !URL.canParse(audience)) return false;
11655
+ const url = new URL(audience);
11656
+ const port = parts[2] ?? "";
11657
+ return url.protocol === "https:" && url.hostname === (parts[1] ?? "").toLowerCase() && url.port === (port === "443" ? "" : port);
11658
+ };
11659
+ /**
11660
+ * Who an identity token is for: an outside service's HTTPS origin, in ASCII, at most 255 bytes,
11661
+ * with no credentials, path, query, fragment, wildcard or space. It's kept exactly as asked, its
11662
+ * case and any port too, so `https://verifier.example:443` is another audience than
11663
+ * `https://verifier.example`. ref0 never fetches or resolves it.
11664
+ */
11665
+ const IdentityAudience = Schema.String.check(Schema.isMaxLength(255), Schema.isPattern(audiencePattern), Schema.makeFilter((audience) => parsesAsWritten(audience) || "an origin the platform reads as written")).pipe(Schema.brand("IdentityAudience"));
11666
+ /** `POST /v1/me/identity-token`'s body: the one audience, and nothing else. */
11667
+ const IdentityTokenRequest = Schema.Struct({ audience: IdentityAudience });
11668
+ /** A compact JWT: its header, payload and signature, each in base64url, at most 4 KiB. */
11669
+ const IdentityToken = Schema.String.check(Schema.isMaxLength(4096), Schema.isPattern(/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/)).pipe(Schema.brand("IdentityToken"));
11670
+ /** What asking for an identity token answers: the token, and when it expires, its `exp`. */
11671
+ const IssuedIdentityToken = Schema.Struct({
11672
+ token: IdentityToken,
11673
+ expiresAt: Schema.DateTimeUtcFromMillis
11674
+ });
11675
+ /**
11676
+ * An identity token's `jti`: a fresh UUIDv4 from the platform's cryptographic source for each
11677
+ * token, naming its issuance in ref0's log without naming any row or secret.
11678
+ */
11679
+ const IdentityTokenId = Schema.String.check(Schema.isPattern(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/)).pipe(Schema.brand("IdentityTokenId"));
11680
+ /**
11681
+ * A person, as one audience knows them: `pperson_`, then the first 128 bits of an HMAC of the
11682
+ * person's ref0 ID for the token's issuer and audience, as a TypeID suffix. Stable at one
11683
+ * audience, different at every other, and carrying no time.
11684
+ */
11685
+ const PairwisePersonId = TypeId("pperson").pipe(Schema.brand("PairwisePersonId"));
11686
+ /** A session, as one audience knows it, made as a person's is. */
11687
+ const PairwiseSessionId = TypeId("psession").pipe(Schema.brand("PairwiseSessionId"));
11688
+ /** The accountable person, last in a token's chain: only their pairwise ID. */
11689
+ const PersonActor = Schema.Struct({ sub: PairwisePersonId });
11690
+ /** A session in a token's chain, then whoever it acts for: its parent session, or the person. */
11691
+ const sessionActing = (act) => Schema.Struct({
11692
+ sub: PairwiseSessionId,
11693
+ act
11694
+ });
11695
+ const oneSession = sessionActing(PersonActor);
11696
+ const twoSessions = sessionActing(oneSession);
11697
+ const threeSessions = sessionActing(twoSessions);
11698
+ /**
11699
+ * A token's `act`, as RFC 8693 nests it: the current session, then up to three parent sessions,
11700
+ * then the accountable person, each by its pairwise ID. It's history, not authority: a verifier
11701
+ * allows the agent in `sub`, never an actor.
11702
+ */
11703
+ const IdentityActor = Schema.Union([
11704
+ oneSession,
11705
+ twoSessions,
11706
+ threeSessions,
11707
+ sessionActing(threeSessions)
11708
+ ]);
11709
+ /** A time in a token, in whole UTC epoch seconds. */
11710
+ const EpochSeconds = Schema.Int.check(Schema.isGreaterThan(0));
11711
+ Schema.Struct({
11712
+ iss: ApiOrigin,
11713
+ sub: AgentId,
11714
+ aud: IdentityAudience,
11715
+ iat: EpochSeconds,
11716
+ exp: EpochSeconds,
11717
+ jti: IdentityTokenId,
11718
+ act: IdentityActor
11719
+ }).check(Schema.makeFilter(({ iat, exp }) => exp > iat && exp - iat <= 600 || `an expiry after its issue, at most 600 seconds later`));
11720
+ /**
11721
+ * A signing key's ID, its `kid`: the key's RFC 7638 thumbprint, SHA-256 in base64url, so one ID
11722
+ * never names two keys.
11723
+ */
11724
+ const SigningKeyId = Schema.String.check(Schema.isPattern(/^[A-Za-z0-9_-]{43}$/)).pipe(Schema.brand("SigningKeyId"));
11725
+ Schema.Struct({
11726
+ alg: Schema.Literal("ES256"),
11727
+ kid: SigningKeyId,
11728
+ typ: Schema.Literal("JWT")
11729
+ });
11730
+ /** A P-256 coordinate: 32 bytes in base64url, unpadded, in its one canonical spelling. */
11731
+ const Coordinate = Schema.String.check(Schema.isPattern(/^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/));
11732
+ /** One public key in the JWKS: exactly these fields, and never a private one. */
11733
+ const PublicSigningKey = Schema.Struct({
11734
+ kty: Schema.Literal("EC"),
11735
+ crv: Schema.Literal("P-256"),
11736
+ x: Coordinate,
11737
+ y: Coordinate,
11738
+ use: Schema.Literal("sig"),
11739
+ alg: Schema.Literal("ES256"),
11740
+ kid: SigningKeyId
11741
+ });
11742
+ Schema.Struct({ keys: Schema.Array(PublicSigningKey).check(Schema.isMaxLength(3)) });
11743
+ Schema.Struct({
11744
+ issuer: ApiOrigin,
11745
+ jwks_uri: Schema.String,
11746
+ subject_types_supported: Schema.Tuple([Schema.Literal("public")]),
11747
+ id_token_signing_alg_values_supported: Schema.Tuple([Schema.Literal("ES256")]),
11748
+ claims_supported: Schema.Tuple([
11749
+ Schema.Literal("iss"),
11750
+ Schema.Literal("sub"),
11751
+ Schema.Literal("aud"),
11752
+ Schema.Literal("exp"),
11753
+ Schema.Literal("iat"),
11754
+ Schema.Literal("jti"),
11755
+ Schema.Literal("act")
11756
+ ])
11757
+ });
11758
+ /**
11759
+ * The audience isn't one ref0 signs for: missing, given twice, or not an HTTPS origin as
11760
+ * `IdentityAudience` says. The refused text is never echoed.
11761
+ */
11762
+ var IdentityAudienceRefused = class extends Schema.TaggedError()("IdentityAudienceRefused", { nextStep: Schema.String }, { httpApiStatus: 400 }) {};
11763
+ /**
11764
+ * Less than a whole second is left before the session, a parent or the credential expires, or
11765
+ * the token would expire before it's recorded: the next prompt renews or replaces the session.
11766
+ */
11767
+ var IdentityTokenNotReady = class extends Schema.TaggedError()("IdentityTokenNotReady", { nextStep: Schema.String }, { httpApiStatus: 409 }) {};
11768
+ /**
11769
+ * ref0 signed an identity token for one of a project's sessions: in that project's log, on the
11770
+ * session's thread, with ref0's own IDs for the session, its agent and its accountable person,
11771
+ * and the token's `jti`, never the token. It proves ref0 made the token, not that any service
11772
+ * received or used it. A failed append discards the token, so every token has its event.
11773
+ */
11774
+ const IdentityTokenIssued = eventKind("identity_token.issued", Schema.Struct({
11775
+ session: SessionId,
11776
+ agent: AgentRef,
11777
+ /** The person whose attachment authorized the session's chain, last in the token's `act`. */
11778
+ accountable: PersonId,
11779
+ jti: IdentityTokenId,
11780
+ audience: IdentityAudience,
11781
+ issuer: ApiOrigin,
11782
+ keyId: SigningKeyId,
11783
+ issuedAt: Schema.DateTimeUtcFromMillis,
11784
+ expiresAt: Schema.DateTimeUtcFromMillis
11785
+ }));
11587
11786
  Schema.Literals([
11588
11787
  "info/refs",
11589
11788
  "git-upload-pack",
@@ -11834,6 +12033,71 @@ const GateAddition = Schema.Struct({
11834
12033
  from: NeedFrom
11835
12034
  });
11836
12035
  /**
12036
+ * A yes that met a need: its decider's, given by the person `by` when one gave it, such as an
12037
+ * admin answering as the owner's admins; or, `overridden`, a check ref0 said no to, met by the
12038
+ * yes of each of its overrides.
12039
+ */
12040
+ const NeedYes = Schema.Struct({
12041
+ decider: DeciderRef,
12042
+ by: Schema.optionalKey(PersonId),
12043
+ overridden: Schema.optionalKey(Schema.Literal(true))
12044
+ });
12045
+ /**
12046
+ * An override left of a check ref0 said no to: of a part's failed run, or of the check not running,
12047
+ * and who answers it.
12048
+ */
12049
+ const OverrideLeft = Schema.Struct({
12050
+ of: Schema.Literals(["failed run", "not run"]),
12051
+ by: OverrideAnswerer
12052
+ });
12053
+ /**
12054
+ * A decider a need waits on. For a check ref0 said no to, `overrides` lists each of its overrides
12055
+ * left, every one of whose yeses meets it in a pass's place, none when it has none: one yes to a
12056
+ * gate answers every decider its person may answer as, each override among them.
12057
+ */
12058
+ const NeedWait = Schema.Struct({
12059
+ decider: DeciderRef,
12060
+ overrides: Schema.optionalKey(Schema.Array(OverrideLeft).check(Schema.isMaxLength(maxGateDeciders)))
12061
+ });
12062
+ /**
12063
+ * How one of a version's needs stands under its gate's answers now, as the gate is decided from
12064
+ * them: `met` by each yes in `by`; `met` with nothing to wait for, `as` `no checks`, for `checks`
12065
+ * when none of the repo's checks runs on the change, or `on the tip`, for a check only the merge
12066
+ * queue's tip runs, which judges it before `main` moves; `waiting` on each decider in `on`, when
12067
+ * `all`, or else on a yes from any one of them; `waiting` for ref0 to read which of `main`'s
12068
+ * checks run on the change; or `refused` by a person's no to one of its deciders, or to an
12069
+ * override of one, which decides the whole gate no.
12070
+ */
12071
+ const NeedStanding = Schema.Union([
12072
+ Schema.Struct({
12073
+ state: Schema.Literal("met"),
12074
+ by: Schema.Array(NeedYes).check(Schema.isMaxLength(maxGateDeciders))
12075
+ }),
12076
+ Schema.Struct({
12077
+ state: Schema.Literal("met"),
12078
+ as: Schema.Literals(["no checks", "on the tip"])
12079
+ }),
12080
+ Schema.Struct({
12081
+ state: Schema.Literal("waiting"),
12082
+ on: Schema.Array(NeedWait).check(Schema.isMaxLength(maxGateDeciders)),
12083
+ all: Schema.Boolean
12084
+ }),
12085
+ Schema.Struct({
12086
+ state: Schema.Literal("waiting"),
12087
+ for: Schema.Literal("checks read")
12088
+ }),
12089
+ Schema.Struct({
12090
+ state: Schema.Literal("refused"),
12091
+ decider: DeciderRef,
12092
+ by: PersonId
12093
+ })
12094
+ ]);
12095
+ /** A version's need, with every place it came from, and how it stands once its gate is open. */
12096
+ const GateNeedView = Schema.Struct({
12097
+ ...GateNeed.fields,
12098
+ standing: Schema.optionalKey(NeedStanding)
12099
+ });
12100
+ /**
11837
12101
  * The version's gate decided again in place under `main`'s `.ref0/` now (Q7c): on which `main`
11838
12102
  * and under its tree, `pending` until `main`'s gate answers, then `held` or `grew`; the needs
11839
12103
  * it answered, joined to ref0's floor, absent while pending; what `main`'s gate read of the
@@ -11935,8 +12199,21 @@ const GateView = Schema.Struct({
11935
12199
  state: GateState,
11936
12200
  /** Why the gate failed; null otherwise. */
11937
12201
  why: Schema.NullOr(Schema.String),
11938
- /** None while pending; once answered, the floor's too, sorted as they're spelled. */
11939
- needs: Schema.Array(GateNeed),
12202
+ /**
12203
+ * None while pending; once answered, the floor's too, sorted as they're spelled, each with how
12204
+ * it stands once the version's gate is open.
12205
+ */
12206
+ needs: Schema.Array(GateNeedView),
12207
+ /** The repo's owner, whose admins answer its admins' needs: at home, its person alone. */
12208
+ owner: OwnerId,
12209
+ /**
12210
+ * The names of the owner and of the people its needs' standings name, where the directory has
12211
+ * them.
12212
+ */
12213
+ names: Schema.optionalKey(Schema.Array(Schema.Struct({
12214
+ owner: OwnerId,
12215
+ name: OwnerName
12216
+ })).check(Schema.isMaxLength(maxGateDeciders + 1))),
11940
12217
  /** What the gate read of the change's trees, each read once, in order; none while pending. */
11941
12218
  reads: GateReads,
11942
12219
  /** The project gate revision the version was decided under; none for 0. */
@@ -14074,6 +14351,7 @@ const KnownEvent = Schema.Union([
14074
14351
  ThreadListingChanged,
14075
14352
  SessionStarted,
14076
14353
  SessionEnded,
14354
+ IdentityTokenIssued,
14077
14355
  MemberRoleChanged,
14078
14356
  MemberRemoved,
14079
14357
  InvitationSent,
@@ -14917,6 +15195,11 @@ var ApprovalSecret = class extends HttpApiMiddleware.Service()("ref0/contracts/A
14917
15195
  * what to change. Any other part that fails its contract is answered with Effect's empty 400.
14918
15196
  */
14919
15197
  var MachineNameRefusal = class extends HttpApiMiddleware.Service()("ref0/contracts/MachineNameRefusal", { error: CommandRefused }) {};
15198
+ /**
15199
+ * An identity token's request whose audience its contract refuses, missing or not an HTTPS
15200
+ * origin, answered as `IdentityAudienceRefused`, which never echoes what was sent.
15201
+ */
15202
+ var IdentityAudienceRefusal = class extends HttpApiMiddleware.Service()("ref0/contracts/IdentityAudienceRefusal", { error: IdentityAudienceRefused }) {};
14920
15203
  const idempotency = { "idempotency-key": RequestId };
14921
15204
  const inProject = {
14922
15205
  owner: OwnerRef,
@@ -15139,7 +15422,18 @@ var MeGroup = class extends HttpApiGroup.make("me").add(HttpApiEndpoint.get("get
15139
15422
  UsageRangeRefused,
15140
15423
  AuthorityUnavailable
15141
15424
  ]
15142
- })).middleware(Authorization) {};
15425
+ }), HttpApiEndpoint.post("identityToken", identityTokenPath, {
15426
+ payload: IdentityTokenRequest,
15427
+ success: IssuedIdentityToken,
15428
+ error: [
15429
+ IdentityAudienceRefused,
15430
+ IdentityTokenNotReady,
15431
+ NotPermitted,
15432
+ SessionHasEnded,
15433
+ SlowDown,
15434
+ AuthorityUnavailable
15435
+ ]
15436
+ }).middleware(IdentityAudienceRefusal)).middleware(Authorization) {};
15143
15437
  /**
15144
15438
  * A thread's endpoints, as one of a project's threads and an org thread take them alike, by
15145
15439
  * where they are: `at` is a project's path parameters, or the owner's alone for its own scope.
@@ -16075,15 +16369,18 @@ const decisionEndpoints = (at) => {
16075
16369
  HttpApiEndpoint.get("get", "/:decision", {
16076
16370
  params: onDecision,
16077
16371
  query: { wait: Schema.optionalKey(DecisionWait) },
16078
- success: DecisionView,
16372
+ success: NamedDecision,
16079
16373
  error: [...projectErrors, DecisionNotFound]
16080
16374
  }),
16081
16375
  HttpApiEndpoint.post("answer", "/:decision/answers", {
16082
16376
  params: onDecision,
16083
16377
  headers: idempotency,
16084
16378
  payload: AnswerRequest,
16085
- success: DecisionView,
16379
+ success: NamedDecision,
16086
16380
  error: [
16381
+ ApprovalNeeded,
16382
+ CommandRefused,
16383
+ SlowDown,
16087
16384
  ...projectErrors,
16088
16385
  DecisionNotFound,
16089
16386
  AnswerRefused,
@@ -16898,6 +17195,15 @@ const ownSurfaces = {
16898
17195
  cli: "meters",
16899
17196
  mcp: null
16900
17197
  },
17198
+ "me.identityToken": {
17199
+ action: "session:change",
17200
+ actors: "agent",
17201
+ target: "session",
17202
+ stepUp: "never",
17203
+ asks: "never",
17204
+ cli: "identity-token",
17205
+ mcp: null
17206
+ },
16901
17207
  "threads.list": {
16902
17208
  action: "project:read",
16903
17209
  actors: "both",
@@ -18328,6 +18634,6 @@ const signOut = (origin) => Effect.gen(function* () {
18328
18634
  yield* client.execute(HttpClientRequest.make(method)(path, { body: HttpBody.jsonUnsafe({}) }));
18329
18635
  });
18330
18636
  //#endregion
18331
- export { AgentHandle, ApiOrigin, AttachmentFailed, AttachmentRequestView, ClientAccess, ClientGranted, ConnectionChanged, ConnectionKind, ConnectionName, ConsentProject, FoldersConnected, LiveTrackName, MachineConnected, OwnerName, OwnerTrackMessage, PasskeyAsserted, PasskeyCreated, PreviewUnavailable, ProjectName, ProjectTrackMessage, RepoName, RepoUrl, SecretHash, SentryOrg, SentryRegion, SentryToken, SignInDestination, ThreadId, ThreadStage, TypedApprovalCode, WorkflowPreview, WrittenProject, anonymousCredential, bearerCredential, browserCredential, commandLine, connectEndpoint, encodeSecret, fetchTransport, followApproval, hookTokenPrefix, hookUrl, isRef0Path, liveMessages, liveSocketUrl, newRequestId, projectParts, ref0Client, repoNameKey, settledOf, signOut, startGitHubSignIn, surfaceOf };
18637
+ export { AgentHandle, AnswerKey, ApiOrigin, AttachmentFailed, AttachmentRequestView, ClientAccess, ClientGranted, ConnectionChanged, ConnectionKind, ConnectionName, ConsentProject, FoldersConnected, LiveTrackName, MachineConnected, OwnerName, OwnerTrackMessage, PasskeyAsserted, PasskeyCreated, PreviewUnavailable, ProjectName, ProjectTrackMessage, RepoName, RepoUrl, SecretHash, SentryOrg, SentryRegion, SentryToken, SignInDestination, ThreadId, ThreadStage, TypedApprovalCode, WorkflowPreview, WrittenProject, anonymousCredential, bearerCredential, browserCredential, commandLine, connectEndpoint, encodeSecret, fetchTransport, followApproval, hookTokenPrefix, hookUrl, isRef0Path, liveMessages, liveSocketUrl, newRequestId, projectParts, ref0Client, repoNameKey, settledOf, signOut, startGitHubSignIn, surfaceOf, threadParts };
18332
18638
 
18333
18639
  //# sourceMappingURL=client.mjs.map