agentplugged 1.0.6 → 1.3.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.
@@ -483,6 +483,11 @@ const ErrorCode = {
483
483
  */
484
484
  DELEGATION_INVALID: "DELEGATION_INVALID",
485
485
  /**
486
+ * `pause_team`: the team is already paused, there is nothing more to hold. Nothing was
487
+ * written. `details: { since }`.
488
+ */
489
+ TEAM_ALREADY_PAUSED: "TEAM_ALREADY_PAUSED",
490
+ /**
486
491
  * A review cannot start, be given, sorted, read or posted (`start_review`,
487
492
  * `submit_review`, `triage_review`, `get_review`, `publish_review`). Nothing was written.
488
493
  * `details: { story, reason, … }` (`REVIEW_NOT_POSSIBLE_REASONS`).
@@ -695,6 +700,16 @@ const EVENT_CONVERSIONS = [
695
700
  from: 13,
696
701
  convert: (payload) => ({ ...payload })
697
702
  },
703
+ {
704
+ type: "checkpoint.opened",
705
+ from: 14,
706
+ convert: (payload) => ({ ...payload })
707
+ },
708
+ {
709
+ type: "checkpoint.opened",
710
+ from: 15,
711
+ convert: (payload) => ({ ...payload })
712
+ },
698
713
  {
699
714
  type: "creator_action.executed",
700
715
  from: 11,
@@ -715,6 +730,26 @@ const EVENT_CONVERSIONS = [
715
730
  from: 2,
716
731
  convert: (payload) => ({ ...payload })
717
732
  },
733
+ {
734
+ type: "story.merged",
735
+ from: 3,
736
+ convert: (payload) => ({ ...payload })
737
+ },
738
+ {
739
+ type: "story.merged",
740
+ from: 4,
741
+ convert: (payload) => ({ ...payload })
742
+ },
743
+ {
744
+ type: "checkpoint.web_answered",
745
+ from: 1,
746
+ convert: (payload) => ({ ...payload })
747
+ },
748
+ {
749
+ type: "checkpoint.web_held",
750
+ from: 1,
751
+ convert: (payload) => ({ ...payload })
752
+ },
718
753
  {
719
754
  type: "checkpoint.closed",
720
755
  from: 1,
@@ -756,19 +791,21 @@ const EVENT_CONVERSIONS = [
756
791
  * raised when a payload gains fields a strict schema of an older engine would refuse: that
757
792
  * engine then asks for an upgrade (`UPGRADE_REQUIRED`) instead of ignoring the event as invalid.
758
793
  * `checkpoint.opened` is at 2 and at 3 and at 4
759
- * and at 5 and at 6 and at 7 and at 8 and at 9 and at 10 and at 11 and at 12 and at 13 and at 14, `requirement.proved` at 2, `story.merged` at 2 and at 3, `cost.reported` at 2,
794
+ * and at 5 and at 6 and at 7 and at 8 and at 9 and at 10 and at 11 and at 12 and at 13 and at 14 and at 15 and at 16, `requirement.proved` at 2, `story.merged` at 2, at 3, at 4 and at 5, `checkpoint.web_answered` and `checkpoint.web_held` at 2, `cost.reported` at 2,
760
795
  * `creator_action.executed` at 2, at 3, at 4 and at 5 and at 6 and at 7 and at 8 and at 9 and at 10 and at 11 and at 12, `checkpoint.closed` at 2 (and `checkpoint.answered` at 2),
761
796
  * `checkpoint.elicited` and `stack.answered` at 2, `usage.measured` and
762
797
  * `agent.status_reported` at 2.
763
798
  */
764
799
  const EVENT_TYPE_VERSIONS = {
765
- "checkpoint.opened": 14,
800
+ "checkpoint.opened": 16,
766
801
  "checkpoint.closed": 2,
767
802
  "checkpoint.answered": 2,
768
803
  "cost.reported": 2,
769
804
  "requirement.proved": 2,
770
805
  "creator_action.executed": 12,
771
- "story.merged": 3,
806
+ "story.merged": 5,
807
+ "checkpoint.web_answered": 2,
808
+ "checkpoint.web_held": 2,
772
809
  "checkpoint.elicited": 2,
773
810
  "stack.answered": 2,
774
811
  "usage.measured": 2,
@@ -785,6 +822,20 @@ function eventConversion(type, version) {
785
822
  return EVENT_CONVERSIONS.find((entry) => entry.type === type && entry.from === version);
786
823
  }
787
824
  //#endregion
825
+ //#region ../../packages/protocol/dist/extension.js
826
+ /** The name of a module: lowercase letters, digits and underscores, starting with a letter. */
827
+ const EXTENSION_MODULE_PATTERN = /^[a-z][a-z0-9_]*$/;
828
+ /** `x.<module>.<type>`, lowercase. */
829
+ const EXTENSION_TYPE_PATTERN = /^x\.[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*$/;
830
+ /** True for a type of the extension point (`x.<module>.<type>`). */
831
+ function isExtensionEventType(type) {
832
+ return EXTENSION_TYPE_PATTERN.test(type);
833
+ }
834
+ /** The module an extension type belongs to, or undefined for any other type. */
835
+ function extensionModuleOf(type) {
836
+ return isExtensionEventType(type) ? type.split(".")[1] : void 0;
837
+ }
838
+ //#endregion
788
839
  //#region ../../packages/protocol/dist/event-envelope.js
789
840
  /** True when `type` can be converted from `version` up to its current version. */
790
841
  function convertible(type, version) {
@@ -860,8 +911,8 @@ const BASE64URL = /^[A-Za-z0-9_-]*$/;
860
911
  /** ULID: 26 Crockford base32 characters, the first one at most 7 (48-bit time). */
861
912
  const ULID_PATTERN = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
862
913
  const ULID = ULID_PATTERN;
863
- /** `domain.action`, lowercase. */
864
- const EVENT_TYPE_PATTERN = /^[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*$/;
914
+ /** `domain.action`, lowercase; or `x.<module>.<type>` for the extension point. */
915
+ const EVENT_TYPE_PATTERN = new RegExp(`^(?:[a-z][a-z0-9_]*\\.[a-z][a-z0-9_]*|${EXTENSION_TYPE_PATTERN.source.slice(1, -1)})$`);
865
916
  /** `sha256:` then the 32-byte digest in base64url (43 characters). */
866
917
  const EVENT_HASH = /^sha256:[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
867
918
  /** 64 bytes in base64url: 86 characters, the last one carrying 2 bits only. */
@@ -915,7 +966,7 @@ const WebAuthnSignatureSchema = z.strictObject({
915
966
  });
916
967
  /** Event envelope v1: one event of the journal or of a validation, signed by its writer. */
917
968
  const EventEnvelopeSchema = z.strictObject({
918
- v: z.number().int().min(0).max(MAX_EVENT_VERSION),
969
+ v: z.number().int().min(0),
919
970
  id: UlidSchema,
920
971
  type: z.string().regex(EVENT_TYPE_PATTERN, { message: EVENT_REASON.eventType }),
921
972
  at: z.iso.datetime({ message: EVENT_REASON.utcDatetime }),
@@ -930,6 +981,11 @@ const EventEnvelopeSchema = z.strictObject({
930
981
  sig: z.union([z.string(), z.record(z.string(), z.unknown())]),
931
982
  assurance: z.enum(ASSURANCE_LEVELS).optional()
932
983
  }).superRefine((event, context) => {
984
+ if (event.v > MAX_EVENT_VERSION && !isExtensionEventType(event.type)) context.addIssue({
985
+ code: "custom",
986
+ path: ["v"],
987
+ message: EVENT_REASON.invalidValue
988
+ });
933
989
  if (event.v < currentVersionOf(event.type) && !convertible(event.type, event.v)) context.addIssue({
934
990
  code: "custom",
935
991
  path: ["v"],
@@ -1040,7 +1096,7 @@ function eventInvalid(field, reasonKey) {
1040
1096
  function parseEventEnvelope(input) {
1041
1097
  if (typeof input === "object" && input !== null && !Array.isArray(input)) {
1042
1098
  const { v: version, type } = input;
1043
- if (typeof version === "number" && Number.isInteger(version) && version > MAX_EVENT_VERSION) return {
1099
+ if (typeof version === "number" && Number.isInteger(version) && version > MAX_EVENT_VERSION && !(typeof type === "string" && isExtensionEventType(type))) return {
1044
1100
  success: false,
1045
1101
  error: {
1046
1102
  code: ErrorCode.UPGRADE_REQUIRED,
@@ -1307,16 +1363,19 @@ const MODEL_TIERS = [
1307
1363
  "multimodal"
1308
1364
  ];
1309
1365
  /**
1310
- * Clients of the model table: the AI tools as they read a sub-agent. `antigravity` and
1311
- * `gemini-cli` are both served by the `antigravity` entry of `tools`.
1366
+ * Clients of the model table: the AI tools as they read a sub-agent.
1312
1367
  */
1313
1368
  const MODEL_CLIENT_IDS = [
1314
1369
  "claude-code",
1315
1370
  "codex",
1316
1371
  "antigravity",
1317
- "gemini-cli",
1318
1372
  "cursor"
1319
1373
  ];
1374
+ /**
1375
+ * Key of the retired Gemini CLI in a model table or in the `models` section of `method.yaml`.
1376
+ * Older products still hold it: it is read, reported and ignored, never accepted for anything new.
1377
+ */
1378
+ const LEGACY_GEMINI_CLIENT = "gemini-cli";
1320
1379
  /** Model value that keeps the model of the session: no model is chosen per agent. */
1321
1380
  const INHERIT_MODEL = "inherit";
1322
1381
  const Value = z.string().trim().min(1);
@@ -1369,7 +1428,7 @@ const ModelTableSchema = z.strictObject({
1369
1428
  "claude-code": tiers(ClaudeCell),
1370
1429
  codex: tiers(CodexCell),
1371
1430
  antigravity: tiers(ModelOnly),
1372
- "gemini-cli": tiers(ModelOnly),
1431
+ [LEGACY_GEMINI_CLIENT]: z.unknown().optional(),
1373
1432
  cursor: tiers(ModelOnly)
1374
1433
  });
1375
1434
  function partialTiers(cell) {
@@ -1394,7 +1453,7 @@ const ModelOverridesSchema = z.strictObject({
1394
1453
  effort: z.enum([...CODEX_EFFORTS, NO_EFFORT]).optional()
1395
1454
  })),
1396
1455
  antigravity: partialTiers(ModelOnly.partial()),
1397
- "gemini-cli": partialTiers(ModelOnly.partial()),
1456
+ [LEGACY_GEMINI_CLIENT]: z.unknown().optional(),
1398
1457
  cursor: partialTiers(ModelOnly.partial())
1399
1458
  });
1400
1459
  const SemverSchema = z.string().regex(/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/);
@@ -1483,6 +1542,48 @@ const ProfileSchema = z.strictObject({
1483
1542
  message: "must be empty while the code domain is inactive"
1484
1543
  });
1485
1544
  });
1545
+ /** The starting profiles. */
1546
+ const PROFILE_PRESET_SPECS = {
1547
+ "new-product": {
1548
+ domains: DOMAIN_IDS,
1549
+ phases: PHASE_IDS,
1550
+ specialties: ["backend", "frontend"]
1551
+ },
1552
+ maintenance: {
1553
+ domains: [
1554
+ "code",
1555
+ "quality",
1556
+ "security",
1557
+ "delivery",
1558
+ "docs"
1559
+ ],
1560
+ phases: [
1561
+ "design",
1562
+ "build",
1563
+ "delivery"
1564
+ ],
1565
+ specialties: ["backend", "frontend"]
1566
+ },
1567
+ "launch-only": {
1568
+ domains: [
1569
+ "product",
1570
+ "creativity",
1571
+ "marketing",
1572
+ "sales"
1573
+ ],
1574
+ phases: ["framing", "launch"],
1575
+ specialties: []
1576
+ },
1577
+ prototype: {
1578
+ domains: [
1579
+ "product",
1580
+ "ux",
1581
+ "code"
1582
+ ],
1583
+ phases: ["framing", "build"],
1584
+ specialties: ["backend", "frontend"]
1585
+ }
1586
+ };
1486
1587
  function idList() {
1487
1588
  return z.array(SkillNameSchema).refine((list) => new Set(list).size === list.length, { message: ISSUE.duplicateValue });
1488
1589
  }
@@ -1561,6 +1662,10 @@ const RulesSchema = z.strictObject({
1561
1662
  });
1562
1663
  /** What going over a story's ceiling does: pause it behind a checkpoint, or flag it. */
1563
1664
  const BUDGET_EXCEED_MODES = ["block", "flag"];
1665
+ /** The alert ratio without a setting: 80 % of the ceiling. */
1666
+ const DEFAULT_ALERT_RATIO = .8;
1667
+ /** What going over the ceiling does without a setting. */
1668
+ const DEFAULT_ON_EXCEED = "block";
1564
1669
  /** The lowest alert ratio a product may set. */
1565
1670
  const MIN_ALERT_RATIO = .05;
1566
1671
  /** `budgets` of `method.yaml`: the alert ratio, and what going over the ceiling does. */
@@ -3590,7 +3695,7 @@ function checkpointIdOf(eventId) {
3590
3695
  /** A free text of 1 to `max` characters. */
3591
3696
  const text = (max) => z.string().trim().min(1).max(max);
3592
3697
  /** A short identifier: an option or a criterion. */
3593
- const ShortIdSchema$3 = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$/, { message: "expected 1 to 32 letters, digits, dots, hyphens or underscores" });
3698
+ const ShortIdSchema$4 = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$/, { message: "expected 1 to 32 letters, digits, dots, hyphens or underscores" });
3594
3699
  /** A measure's name: `tests_failed`, `coverage`, `lighthouse.performance`. */
3595
3700
  const MetricSchema = z.string().regex(/^[a-z][a-z0-9_.]{0,63}$/, { message: "expected a lowercase name of letters, digits, dots and underscores" });
3596
3701
  /**
@@ -3619,13 +3724,13 @@ const CheckpointBlockSchema = z.strictObject({
3619
3724
  });
3620
3725
  /** One answer of a decision: what it is, and what it entails. */
3621
3726
  const CheckpointOptionSchema = z.strictObject({
3622
- id: ShortIdSchema$3,
3727
+ id: ShortIdSchema$4,
3623
3728
  label: text(200),
3624
3729
  consequence: text(1e3)
3625
3730
  });
3626
3731
  /** A rule on a measure: `<metric> <op> <threshold>`. */
3627
3732
  const CheckpointCriterionSchema = z.strictObject({
3628
- id: ShortIdSchema$3,
3733
+ id: ShortIdSchema$4,
3629
3734
  metric: MetricSchema,
3630
3735
  op: z.enum(CRITERION_OPERATORS),
3631
3736
  threshold: z.number().finite(),
@@ -3633,16 +3738,38 @@ const CheckpointCriterionSchema = z.strictObject({
3633
3738
  });
3634
3739
  /** Measures reported for a checkpoint: `{ <metric>: number }`, 1 to 50 of them. */
3635
3740
  const MeasuresSchema = z.record(MetricSchema, z.number().finite()).refine((measures) => Object.keys(measures).length >= 1, { message: "give at least a measure" }).refine((measures) => Object.keys(measures).length <= 50, { message: "give at most 50 measures" });
3741
+ /**
3742
+ * What the creator reads first on a checkpoint, in plain words: what changes for the people who
3743
+ * use the product (`effect`), what could go wrong (`risks`) and what the agents consulted think
3744
+ * (`opinions`, one sentence each). Optional: a checkpoint without a card is read from its
3745
+ * `summary`. The fuller text stays in `summary`, one click away. `why` answers "why this?" in
3746
+ * place: one sentence for the reason, one for the alternative set aside.
3747
+ */
3748
+ const CheckpointCardSchema = z.strictObject({
3749
+ effect: text(300),
3750
+ why: z.strictObject({
3751
+ reason: text(200),
3752
+ /** Absent when nothing else was weighed. */
3753
+ rejected: text(200).optional()
3754
+ }).optional(),
3755
+ risks: z.array(text(200)).min(1).max(3).optional(),
3756
+ opinions: z.array(z.strictObject({
3757
+ agent: AgentIdSchema.max(64),
3758
+ view: text(200)
3759
+ })).min(1).max(4).optional()
3760
+ });
3636
3761
  /** The fields an agent gives, whose defaults `core` fixes (recipient, confirmation). */
3637
3762
  const describedFields = {
3638
3763
  type: z.enum(CHECKPOINT_TYPES),
3639
3764
  risk: z.enum(CHECKPOINT_RISKS),
3640
3765
  title: text(120),
3641
3766
  summary: text(2e3),
3767
+ /** The first lines the creator reads: the effect for the users, the risks, the agents' views. */
3768
+ card: CheckpointCardSchema.optional(),
3642
3769
  documents: z.array(CheckpointDocumentSchema).max(20),
3643
3770
  blocks: z.array(CheckpointBlockSchema).max(10),
3644
3771
  options: z.array(CheckpointOptionSchema).min(2).max(4).optional(),
3645
- recommendation: ShortIdSchema$3.optional(),
3772
+ recommendation: ShortIdSchema$4.optional(),
3646
3773
  criteria: z.array(CheckpointCriterionSchema).min(1).max(20).optional(),
3647
3774
  /** The phase a `phase_end` checkpoint closes: given with that type, and only with it. */
3648
3775
  phase: z.enum(PHASE_IDS).optional()
@@ -3903,7 +4030,7 @@ const NOTE_REQUIRED = /* @__PURE__ */ new Set([
3903
4030
  const CheckpointResolvedPayloadSchema = z.strictObject({
3904
4031
  checkpoint: CheckpointIdSchema,
3905
4032
  decision: z.enum(RESOLUTION_DECISIONS),
3906
- option: ShortIdSchema$3.optional(),
4033
+ option: ShortIdSchema$4.optional(),
3907
4034
  note: text(2e3).optional(),
3908
4035
  waiver_until: z.iso.datetime().optional(),
3909
4036
  source: z.enum(RESOLUTION_SOURCES)
@@ -4807,6 +4934,117 @@ const QaReportedPayloadSchema = z.strictObject({
4807
4934
  path: ["results"]
4808
4935
  });
4809
4936
  //#endregion
4937
+ //#region ../../packages/protocol/dist/account.js
4938
+ /**
4939
+ * The wire contract of the account routes of the relay: creating an account (the address is proved
4940
+ * by a code sent by e-mail, then a passkey is made), signing in with a passkey, reading and
4941
+ * changing the profile, signing out. No password exists anywhere in it: a passkey is the only way
4942
+ * in. The address is unique: one person, one account.
4943
+ */
4944
+ /** The languages an account can choose. */
4945
+ const ACCOUNT_LOCALES = [
4946
+ "fr",
4947
+ "en",
4948
+ "es",
4949
+ "pt"
4950
+ ];
4951
+ /** An address as the relay keeps it: trimmed and lowercased. */
4952
+ const AccountEmailSchema = z.string().transform((value) => value.trim().toLowerCase()).pipe(z.string().max(254).regex(/^[^\s@]+@[^\s@]+\.[^\s@]+$/));
4953
+ const AccountLocaleSchema = z.enum(ACCOUNT_LOCALES);
4954
+ z.strictObject({
4955
+ email: AccountEmailSchema,
4956
+ locale: AccountLocaleSchema
4957
+ });
4958
+ z.strictObject({
4959
+ verification: z.string().min(1).max(64),
4960
+ /** How long the code works, in minutes. */
4961
+ valid_minutes: z.number().int().positive()
4962
+ });
4963
+ z.strictObject({
4964
+ verification: z.string().min(1).max(64),
4965
+ code: z.string().regex(new RegExp(`^[0-9]{6}$`))
4966
+ });
4967
+ z.strictObject({ verification: z.string().min(1).max(64) });
4968
+ z.strictObject({});
4969
+ z.object({
4970
+ ceremony: z.string().min(1).max(64),
4971
+ options: z.record(z.string(), z.unknown())
4972
+ });
4973
+ z.strictObject({
4974
+ ceremony: z.string().min(1).max(64),
4975
+ response: z.record(z.string(), z.unknown())
4976
+ });
4977
+ z.strictObject({ ok: z.literal(true) });
4978
+ z.strictObject({
4979
+ ok: z.literal(true),
4980
+ session: z.string().min(1).max(128),
4981
+ expires_at: z.string()
4982
+ });
4983
+ z.strictObject({
4984
+ account_id: z.string().regex(ULID_PATTERN),
4985
+ email: z.string().nullable(),
4986
+ locale: AccountLocaleSchema.nullable(),
4987
+ created_at: z.string()
4988
+ });
4989
+ z.strictObject({ locale: AccountLocaleSchema });
4990
+ /** An export of the account's data, as the relay describes it to its owner. */
4991
+ const AccountExportStatusSchema = z.strictObject({
4992
+ id: z.string().regex(ULID_PATTERN),
4993
+ status: z.enum(["pending", "ready"]),
4994
+ requested_at: z.string(),
4995
+ /** The date the export is promised by (it is usually ready within seconds). */
4996
+ due_at: z.string(),
4997
+ ready_at: z.string().nullable(),
4998
+ /** The date the link stops working, once the export is ready. */
4999
+ expires_at: z.string().nullable()
5000
+ });
5001
+ z.strictObject({ export: AccountExportStatusSchema.nullable() });
5002
+ z.strictObject({ id: z.string().regex(ULID_PATTERN) });
5003
+ z.strictObject({ confirm: z.literal(true) });
5004
+ /** One passkey of the account, as its owner sees it: public facts only. */
5005
+ const AccountPasskeySchema = z.strictObject({
5006
+ /** The credential identifier, base64url (public: it is not a secret). */
5007
+ id: z.string().min(1).max(1024),
5008
+ created_at: z.string(),
5009
+ last_used_at: z.string().nullable()
5010
+ });
5011
+ z.strictObject({ passkeys: z.array(AccountPasskeySchema).max(10) });
5012
+ z.strictObject({ id: z.string().min(1).max(1024) });
5013
+ //#endregion
5014
+ //#region ../../packages/protocol/dist/account-export.js
5015
+ /**
5016
+ * The file a creator receives when they export their account: everything the relay holds about it,
5017
+ * in indented JSON, checked by this schema (`account-export.v1`). It never holds a secret and never
5018
+ * a readable content: the encrypted packets and vaults appear with their dates and sizes only, and
5019
+ * the relay cannot read them. What a product holds (documents, stories, history) lives in the
5020
+ * creator's repository and is not part of it.
5021
+ */
5022
+ const ACCOUNT_EXPORT_SCHEMA_ID = "account-export.v1";
5023
+ const Scalar = z.union([
5024
+ z.string(),
5025
+ z.number(),
5026
+ z.boolean(),
5027
+ z.null()
5028
+ ]);
5029
+ const Row = z.record(z.string(), Scalar);
5030
+ z.strictObject({
5031
+ schema: z.literal(ACCOUNT_EXPORT_SCHEMA_ID),
5032
+ generated_at: z.string(),
5033
+ /** What is not in this file, and where it is. */
5034
+ not_included: z.string(),
5035
+ account: Row,
5036
+ passkeys: z.array(Row),
5037
+ sessions: z.array(Row),
5038
+ devices: z.array(Row),
5039
+ device_prekeys: z.array(Row),
5040
+ pending_envelopes: z.array(Row),
5041
+ seen_envelopes: z.array(Row),
5042
+ vaults: z.array(Row),
5043
+ shares: z.array(Row),
5044
+ push_tokens: z.array(Row),
5045
+ exports: z.array(Row)
5046
+ });
5047
+ //#endregion
4810
5048
  //#region ../../packages/protocol/dist/skill-definition.js
4811
5049
  /** Risk level of an agent's actions. */
4812
5050
  const RISK_LEVELS = [
@@ -4952,49 +5190,93 @@ const AgentOverrideSchema = z.strictObject({
4952
5190
  voice: customisable.voice.optional()
4953
5191
  });
4954
5192
  //#endregion
4955
- //#region ../../packages/protocol/dist/budget.js
4956
- /**
4957
- * Budgets: each story's ceiling comes from the journal (the last sprint approved that
4958
- * holds it, or the creator's answer to a budget checkpoint); its cost from the agents' declared
4959
- * costs (`cost.reported`, encrypted). When a declared cost reaches the alert ratio, or goes over
4960
- * the ceiling, `core` writes `budget.threshold_reached` (no amount of cost in it), right after
4961
- * that cost in the same write. Over the ceiling, with `on_exceed: block`, it opens a budget
4962
- * checkpoint in between, and the story's write tools answer `BUDGET_EXCEEDED` until the creator
4963
- * answers it.
4964
- */
4965
- /** Event types of budgets, each at version 1. */
4966
- const BUDGET_EVENT_TYPES = { thresholdReached: "budget.threshold_reached" };
4967
- /** The thresholds a story's cost may reach: the alert ratio, then the ceiling itself. */
4968
- const BUDGET_LEVELS = ["alert", "exceeded"];
4969
- /** The largest ceiling, in US dollars (raising a ceiling stops there). */
4970
- const MAX_BUDGET_USD = 1e9;
4971
- /** The prefix of a budget checkpoint's title, before the story. */
4972
- const BUDGET_TITLE_PREFIX = "Budget exceeded: ";
4973
- /**
4974
- * The source of a ceiling: the sprint (`S-…`) whose approval set it, or the budget checkpoint
4975
- * (`G-…`) whose answer did. A threshold counts only for the ceiling it names.
4976
- */
4977
- const CeilingIdSchema = z.string().regex(/^[SG]-[0-9A-HJKMNP-TV-Z]{10}$/, { message: "expected the id of a sprint (S-…) or of a checkpoint (G-…)" });
5193
+ //#region ../../packages/protocol/dist/autonomy.js
5194
+ /**
5195
+ * The autonomy dial of a product: three notches that set, in one gesture, how much the creator
5196
+ * keeps for themselves. The dial is a preset over settings the engine already has (the validation
5197
+ * level, the points the creator keeps, the installs mode, two permission rules); it adds no rule
5198
+ * of its own. Raising it never lifts a locked rule, the irreversible commands or the base guards
5199
+ * (secrets, pushes to the main branch), and never makes an agent validate for the creator:
5200
+ * the merge, the release and the publications are not touched by any notch.
5201
+ *
5202
+ * Everything here is data and pure functions: the machine writes the settings with the engine's own
5203
+ * writers, and the web view reads the same table to say what a notch changes.
5204
+ */
5205
+ /** The notches, from the most careful to the most free. */
5206
+ const AUTONOMY_DIALS = [
5207
+ "prudent",
5208
+ "balanced",
5209
+ "wide"
5210
+ ];
5211
+ const AutonomyDialSchema = z.enum(AUTONOMY_DIALS);
5212
+ /** What a product's settings add up to: a notch, or `custom` (set by hand or from a terminal). */
5213
+ const AUTONOMY_DIAL_STATES = [...AUTONOMY_DIALS, "custom"];
5214
+ const AutonomyDialStateSchema = z.enum(AUTONOMY_DIAL_STATES);
5215
+ /** The points of validation the dial can hand to the agents (the others are never touched). */
5216
+ const AUTONOMY_POINTS = [
5217
+ "framing",
5218
+ "architecture",
5219
+ "breakdown"
5220
+ ];
4978
5221
  /**
4979
- * `payload` of `budget.threshold_reached`: the level reached, the ceiling it was measured against
4980
- * (its amount and its source, never the cost), the settings in force, and the budget checkpoint
4981
- * opened just before it in the same write (`exceeded` under `block` only; absent when one is
4982
- * already open).
4983
- */
4984
- const BudgetThresholdReachedPayloadSchema = z.strictObject({
4985
- level: z.enum(BUDGET_LEVELS),
4986
- budget_usd: z.number().finite().min(0).max(MAX_BUDGET_USD),
4987
- ceiling: CeilingIdSchema,
4988
- alert_ratio: z.number().finite().min(MIN_ALERT_RATIO).max(1),
4989
- on_exceed: z.enum(BUDGET_EXCEED_MODES),
4990
- checkpoint: CheckpointIdSchema.optional()
4991
- }).superRefine((value, context) => {
4992
- if (value.checkpoint !== void 0 && (value.level !== "exceeded" || value.on_exceed !== "block")) context.addIssue({
4993
- code: "custom",
4994
- path: ["checkpoint"],
4995
- message: "a checkpoint goes only with an exceeded level under block"
4996
- });
5222
+ * - `none`: any value.
5223
+ * - `never_allow`: `ask` or `deny` only (the commands that erase data).
5224
+ * - `locked`: `deny` only (the push to the main branch, the force push, the secrets).
5225
+ */
5226
+ const RULE_LOCKS = {
5227
+ read_files: "none",
5228
+ edit_code: "none",
5229
+ run_checks: "none",
5230
+ install_dependency: "none",
5231
+ internet: "none",
5232
+ destructive_command: "never_allow",
5233
+ push_main: "locked",
5234
+ force_push: "locked",
5235
+ read_secrets: "locked"
5236
+ };
5237
+ const ThresholdSchema = z.int().min(3).max(50);
5238
+ /** Rule values by action: every action has one. */
5239
+ const RuleTableSchema = z.strictObject({
5240
+ read_files: z.enum(RULE_VALUES),
5241
+ edit_code: z.enum(RULE_VALUES),
5242
+ run_checks: z.enum(RULE_VALUES),
5243
+ install_dependency: z.enum(RULE_VALUES),
5244
+ internet: z.enum(RULE_VALUES),
5245
+ destructive_command: z.enum(RULE_VALUES),
5246
+ push_main: z.enum(RULE_VALUES),
5247
+ force_push: z.enum(RULE_VALUES),
5248
+ read_secrets: z.enum(RULE_VALUES)
5249
+ });
5250
+ /**
5251
+ * What a machine says of the autonomy of a product it serves. `dial`, `mode`, `installs`, `kept`
5252
+ * and `rules` are what the product's `method.yaml` records now. The engine reads these settings on
5253
+ * the main branch: when what is recorded is not in force yet, `in_force` says what is (the notch
5254
+ * and the rules that differ), and the web view does not pretend otherwise.
5255
+ */
5256
+ const ProductAutonomySchema = z.strictObject({
5257
+ dial: AutonomyDialStateSchema,
5258
+ mode: z.enum(VALIDATION_MODES),
5259
+ installs: z.enum(INSTALL_MODES),
5260
+ kept: z.array(z.enum(VALIDATION_POINTS)).max(VALIDATION_POINTS.length),
5261
+ rules: RuleTableSchema,
5262
+ in_force: z.strictObject({
5263
+ dial: AutonomyDialStateSchema,
5264
+ rules: z.partialRecord(z.enum(RULE_ACTIONS), z.enum(RULE_VALUES))
5265
+ }).optional(),
5266
+ /** Validations without a change after which a notch is proposed. */
5267
+ threshold: ThresholdSchema,
5268
+ /** Per kind of point, when the creator said « not now » to the proposal. */
5269
+ snoozed: z.partialRecord(z.enum(AUTONOMY_POINTS), z.iso.datetime()).optional()
5270
+ });
5271
+ z.strictObject({ dial: AutonomyDialSchema });
5272
+ z.strictObject({
5273
+ action: z.enum(RULE_ACTIONS),
5274
+ value: z.enum(RULE_VALUES)
4997
5275
  });
5276
+ z.strictObject({
5277
+ snooze: z.enum(AUTONOMY_POINTS).optional(),
5278
+ threshold: ThresholdSchema.optional()
5279
+ }).refine((params) => params.snooze !== void 0 || params.threshold !== void 0, { message: "a kind of point to put off, or a threshold" });
4998
5280
  //#endregion
4999
5281
  //#region ../../packages/protocol/dist/question-tool.js
5000
5282
  /**
@@ -5080,7 +5362,7 @@ const ChatProofSchema = z.strictObject({
5080
5362
  });
5081
5363
  /** The creator's words, as quoted: 1 to 4000 characters, never only blanks. */
5082
5364
  const QuoteSchema$1 = z.string().max(MAX_QUOTE_LENGTH).trim().min(1);
5083
- const ShortIdSchema$2 = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$/, { message: "expected 1 to 32 letters, digits, dots, hyphens or underscores" });
5365
+ const ShortIdSchema$3 = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$/, { message: "expected 1 to 32 letters, digits, dots, hyphens or underscores" });
5084
5366
  /** A tool session id as a hook gives it: printable, at most 200 characters. */
5085
5367
  const ToolSessionIdSchema = z.string().trim().min(1).max(200).regex(/^[\x21-\x7e]+$/, { message: "expected printable characters without spaces" });
5086
5368
  /**
@@ -5098,7 +5380,7 @@ const AnswerCheckpointInputSchema = z.strictObject({
5098
5380
  agent: AgentIdSchema.max(64),
5099
5381
  checkpoint: z.string().trim().toUpperCase().pipe(CheckpointIdSchema),
5100
5382
  decision: z.enum(ANSWER_DECISIONS).optional(),
5101
- option: ShortIdSchema$2.optional(),
5383
+ option: ShortIdSchema$3.optional(),
5102
5384
  note: z.string().trim().min(1).max(2e3).optional(),
5103
5385
  quote: QuoteSchema$1.optional(),
5104
5386
  session: ToolSessionIdSchema.optional(),
@@ -5159,7 +5441,7 @@ const AnswerCheckpointInputSchema = z.strictObject({
5159
5441
  const CheckpointAnsweredPayloadSchema = z.strictObject({
5160
5442
  checkpoint: CheckpointIdSchema,
5161
5443
  decision: z.enum(CHAT_DECISIONS),
5162
- option: ShortIdSchema$2.optional(),
5444
+ option: ShortIdSchema$3.optional(),
5163
5445
  note: z.string().trim().min(1).max(2e3).optional(),
5164
5446
  source: z.literal("chat"),
5165
5447
  quote: QuoteSchema$1,
@@ -5184,7 +5466,7 @@ const CheckpointAnsweredPayloadSchema = z.strictObject({
5184
5466
  const CheckpointAutoAppliedPayloadSchema = z.strictObject({
5185
5467
  checkpoint: CheckpointIdSchema,
5186
5468
  decision: z.literal("option_chosen"),
5187
- option: ShortIdSchema$2
5469
+ option: ShortIdSchema$3
5188
5470
  });
5189
5471
  /**
5190
5472
  * `payload` of `checkpoint.reopened`: an automatic application cancelled. From the chat, it
@@ -5216,72 +5498,685 @@ const ANTICIPATED_ANSWER_WINDOW_MS = 18e5;
5216
5498
  /** How long the hook keeps the creator's messages, and how recent a quoted one must be. */
5217
5499
  const CHAT_RETENTION_MS = 864e5;
5218
5500
  //#endregion
5219
- //#region ../../packages/protocol/dist/checkpoint-closed.js
5501
+ //#region ../../packages/protocol/dist/device-trust.js
5220
5502
  /**
5221
- * A point closed by the engine itself: when what a checkpoint asks about is already
5222
- * settled, nobody has to answer it. The engine writes `checkpoint.closed` with the reason, and the
5223
- * projection checks the reason against the journal (a point that is still waiting for something is
5224
- * never closed). The creator is never shown a point whose object is settled.
5503
+ * The devices of trust in the journal. A product's
5504
+ * registry of trusted devices is its journal: `device.paired` says "this key is trusted",
5505
+ * `device.revoked` takes it back, `device.confirmed` ends the provisional state of a device whose
5506
+ * handover is complete, `device.root_certified` records that the first phone recognised the root,
5507
+ * and `trust.dismissed` clears a `SECURITY_ROOT_CHANGED` alert. The chain of authorizations must
5508
+ * lead to the root the bridge pinned locally, outside the repository: never to an event's ULID.
5509
+ *
5510
+ * Generic on purpose: schemas and rules only, no transport, no account, no offer.
5225
5511
  */
5226
- /** Event type of the closing of a checkpoint by the engine, at version 1. */
5227
- const CHECKPOINT_CLOSED_EVENT_TYPES = { closed: "checkpoint.closed" };
5228
- /** `payload` of `checkpoint.closed`: the checkpoint, why, and the newer one for `replaced`. */
5229
- const CheckpointClosedPayloadSchema = z.strictObject({
5230
- checkpoint: CheckpointIdSchema,
5231
- reason: z.enum([
5232
- "story_merged",
5233
- "review_redone",
5234
- "replaced",
5235
- "step_skipped",
5236
- "block_lifted",
5237
- "worktree_gone",
5238
- "lapsed"
5239
- ]),
5240
- /** The checkpoint that replaces it: with `replaced`, and only with it. */
5241
- ref: CheckpointIdSchema.optional()
5242
- }).refine((payload) => payload.reason === "replaced" === (payload.ref !== void 0), {
5243
- message: "a ref goes with replaced, and only with it",
5244
- path: ["ref"]
5245
- });
5246
- //#endregion
5247
- //#region ../../packages/protocol/dist/checkpoint-decided.js
5512
+ /** Event types of the devices of trust, each at version 1. */
5513
+ const DEVICE_TRUST_EVENT_TYPES = {
5514
+ paired: "device.paired",
5515
+ revoked: "device.revoked",
5516
+ rootCertified: "device.root_certified",
5517
+ confirmed: "device.confirmed",
5518
+ dismissed: "trust.dismissed"
5519
+ };
5248
5520
  /**
5249
- * A point decided by the agent responsible for it. The creator keeps for
5250
- * themselves the points they chose (`validation.points` of `method.yaml`); any other point is
5251
- * decided by an agent, which says why. The decision is written as the agent (`actor.kind: agent`,
5252
- * signed by the machine key), never as the creator: its source and its assurance are `agent`, so
5253
- * the history, the morning summary and every surface always tell it apart from an answer of the
5254
- * creator, and the creator can reopen it at any time.
5521
+ * The types the engine writes as a system actor (the bridge, or a creator's terminal for the
5522
+ * first ones): refused in any write by an agent. `trust.dismissed` is the creator's (signed by
5523
+ * the creator's key).
5255
5524
  */
5256
- /** Event type of a point decided by an agent, at version 1. */
5257
- const CHECKPOINT_DECIDED_EVENT_TYPES = { decided: "checkpoint.decided" };
5525
+ const DEVICE_TRUST_SYSTEM_EVENT_TYPES = /* @__PURE__ */ new Set([
5526
+ DEVICE_TRUST_EVENT_TYPES.paired,
5527
+ DEVICE_TRUST_EVENT_TYPES.revoked,
5528
+ DEVICE_TRUST_EVENT_TYPES.rootCertified,
5529
+ DEVICE_TRUST_EVENT_TYPES.confirmed
5530
+ ]);
5531
+ new Set(Object.values(DEVICE_TRUST_EVENT_TYPES));
5532
+ /** The kinds of device a `device.paired` registers (the creator's key has its own events). */
5533
+ const PAIRED_DEVICE_KINDS = [
5534
+ "machine",
5535
+ "phone",
5536
+ "browser"
5537
+ ];
5538
+ /** `machine:`, `human:`, `phone:` or `browser:` then 16 base64url characters. */
5539
+ const DEVICE_ID_PATTERN = /^(machine|human|phone|browser):[A-Za-z0-9_-]{16}$/;
5540
+ /** The identifier of a device `device.paired` registers. */
5541
+ const PAIRED_DEVICE_ID_PATTERN = /^(machine|phone|browser):[A-Za-z0-9_-]{16}$/;
5542
+ /** The identifier of a bridge. */
5543
+ const BRIDGE_ID_PATTERN = /^machine:[A-Za-z0-9_-]{16}$/;
5544
+ /** The kind of identifier (`machine`, `human`, `phone`, `browser`), or undefined when malformed. */
5545
+ function deviceKindOf(device) {
5546
+ if (!DEVICE_ID_PATTERN.test(device)) return void 0;
5547
+ return device.slice(0, device.indexOf(":"));
5548
+ }
5549
+ /** The algorithms of the linked devices (the `webauthn-*` ones serve the terminal's key only). */
5550
+ const DEVICE_SIG_ALG = "ed25519";
5551
+ const DEVICE_ENC_ALG = "x25519";
5552
+ /** 32 bytes in base64url: 43 characters, the last one carrying 4 bits only. */
5553
+ const PUBLIC_KEY_32 = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
5554
+ /** 16 random bytes in base64url: 22 characters, the last one carrying 2 bits only. */
5555
+ const RANDOM_16 = /^[A-Za-z0-9_-]{21}[AQgw]$/;
5556
+ /** `sha256:` then the 32-byte digest in base64url (an event's hash). */
5557
+ const TRUST_EVENT_HASH_PATTERN = /^sha256:[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
5558
+ const publicKey32 = z.string().regex(PUBLIC_KEY_32).refine(isCanonicalBase64url);
5559
+ const utcDatetime$1 = z.iso.datetime();
5560
+ /** A public key of a device with its algorithm: `{ alg, key }`. */
5561
+ const DeviceSigKeySchema = z.strictObject({
5562
+ alg: z.literal(DEVICE_SIG_ALG),
5563
+ key: publicKey32
5564
+ });
5565
+ const DeviceEncKeySchema = z.strictObject({
5566
+ alg: z.literal(DEVICE_ENC_ALG),
5567
+ key: publicKey32
5568
+ });
5258
5569
  /**
5259
- * What `decide_checkpoint` may be given: a decision, or `escalate`, the agent handing the point back
5260
- * to the creator with its reason when it cannot or should not decide it (a merge the forge refuses
5261
- * and nobody can fix, a point it finds beyond its remit). The point then waits for the creator.
5570
+ * Every signed structure of trust carries its label `t` (type and version), and a key signs the
5571
+ * types it knows only: an authorization is never taken for an order, nor a relay's challenge for
5572
+ * an authorization. The domain separation stops a signature made for one purpose from
5573
+ * serving another.
5262
5574
  */
5263
- const DECIDE_CHOICES = [...CHAT_DECISIONS, "escalate"];
5264
- const ShortIdSchema$1 = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$/, { message: "expected 1 to 32 letters, digits, dots, hyphens or underscores" });
5265
- const ReasonSchema$1 = z.string().trim().min(1).max(1e3);
5266
- function checkOption(value, context) {
5267
- if (value.decision === "option_chosen" !== (value.option !== void 0)) context.addIssue({
5268
- code: "custom",
5269
- path: ["option"],
5270
- message: "an option goes with option_chosen, and only with it"
5271
- });
5575
+ const TRUST_SIGNATURE_TAGS = {
5576
+ devicePaired: "device.paired.auth/v1",
5577
+ deviceRevoked: "device.revoked.auth/v1",
5578
+ pairOffer: "pair.offer/v1",
5579
+ rootCertified: "device.root_certified.auth/v1",
5580
+ message: "msg.inner/v1",
5581
+ linkWelcome: "link.welcome/v1",
5582
+ vaultBlob: "vault.blob/v1"
5583
+ };
5584
+ /** True for a label this engine knows. */
5585
+ function isTrustSignatureTag(tag) {
5586
+ return Object.values(TRUST_SIGNATURE_TAGS).includes(tag);
5272
5587
  }
5273
5588
  /**
5274
- * Input of `decide_checkpoint`: the agent that decides, the point, its decision and the reason.
5275
- * `option` goes with `option_chosen` only.
5589
+ * The order of a trusted device that offered a pairing (`pair.offer`): one rendezvous
5590
+ * `x`, valid until `expires_at`, for one device of `kind`. The bridge writes `device.paired` on
5591
+ * its authority, once.
5276
5592
  */
5277
- const DecideCheckpointInputSchema = z.strictObject({
5278
- agent: AgentIdSchema.max(64),
5279
- checkpoint: z.string().trim().toUpperCase().pipe(CheckpointIdSchema),
5280
- decision: z.enum(DECIDE_CHOICES),
5281
- option: ShortIdSchema$1.optional(),
5282
- reason: ReasonSchema$1
5283
- }).superRefine(checkOption);
5284
- /** `payload` of `checkpoint.decided`: the point, the decision and the agent's reason. */
5593
+ const PairOfferSchema = z.strictObject({
5594
+ /** The rendezvous: 16 random bytes in base64url, or the 8-symbol address of a short code. */
5595
+ x: z.union([z.string().regex(RANDOM_16).refine(isCanonicalBase64url), z.string().regex(/^[0-9A-HJKMNP-TV-Z]{8}$/)]),
5596
+ expires_at: utcDatetime$1
5597
+ });
5598
+ /**
5599
+ * The proof that the creator wanted this: the signature, by a device already trusted (or by the
5600
+ * root, from the command run on the root machine), of the record of the event (`order` absent) or
5601
+ * of the signed order `pair.offer` (`order` present).
5602
+ */
5603
+ const AuthorizationSchema = z.strictObject({
5604
+ signer: z.string().regex(DEVICE_ID_PATTERN),
5605
+ alg: z.literal(DEVICE_SIG_ALG),
5606
+ sig: Ed25519SignatureSchema,
5607
+ order: PairOfferSchema.optional()
5608
+ });
5609
+ /** `payload` of `device.paired` v1. */
5610
+ const DevicePairedPayloadSchema = z.strictObject({
5611
+ device: z.string().regex(PAIRED_DEVICE_ID_PATTERN),
5612
+ kind: z.enum(PAIRED_DEVICE_KINDS),
5613
+ sig: DeviceSigKeySchema,
5614
+ enc: DeviceEncKeySchema,
5615
+ /** The bridge of the perimeter: the authorization holds in the journals this bridge writes. */
5616
+ bridge: z.string().regex(BRIDGE_ID_PATTERN),
5617
+ /** True until the handover is complete (`device.confirmed`): the bridge alone may revoke it. */
5618
+ provisional: z.boolean(),
5619
+ issued_at: utcDatetime$1,
5620
+ authorization: AuthorizationSchema
5621
+ }).refine((payload) => payload.device.startsWith(`${payload.kind}:`), {
5622
+ path: ["kind"],
5623
+ message: "the kind is the prefix of the device"
5624
+ });
5625
+ /** `payload` of `device.revoked` v1. */
5626
+ const DeviceRevokedPayloadSchema = z.strictObject({
5627
+ device: z.string().regex(PAIRED_DEVICE_ID_PATTERN),
5628
+ /**
5629
+ * For a machine that writes to the journal: the hash of its last event the author knew. The
5630
+ * projection accepts of a revoked machine only the ancestors of this head (and of the heads of
5631
+ * the other revocations of it). Null for a device that writes nothing.
5632
+ */
5633
+ chain_head: z.string().regex(TRUST_EVENT_HASH_PATTERN).nullable(),
5634
+ issued_at: utcDatetime$1,
5635
+ author: z.string().regex(DEVICE_ID_PATTERN),
5636
+ authorization: z.strictObject({
5637
+ alg: z.literal(DEVICE_SIG_ALG),
5638
+ sig: Ed25519SignatureSchema
5639
+ })
5640
+ }).refine((payload) => payload.chain_head === null || payload.device.startsWith("machine:"), {
5641
+ path: ["chain_head"],
5642
+ message: "only a machine has a chain head"
5643
+ });
5644
+ /** `payload` of `device.root_certified` v1: the first phone recognises the root. */
5645
+ const DeviceRootCertifiedPayloadSchema = z.strictObject({
5646
+ root: z.strictObject({
5647
+ device: z.string().regex(DEVICE_ID_PATTERN),
5648
+ key: publicKey32
5649
+ }),
5650
+ issued_at: utcDatetime$1,
5651
+ authorization: AuthorizationSchema
5652
+ });
5653
+ /** `payload` of `device.confirmed` v1: the handover of the device is complete. */
5654
+ const DeviceConfirmedPayloadSchema = z.strictObject({
5655
+ device: z.string().regex(PAIRED_DEVICE_ID_PATTERN),
5656
+ issued_at: utcDatetime$1
5657
+ });
5658
+ /** `payload` of `trust.dismissed` v1: the events (their hashes) the creator dismisses. */
5659
+ const TrustDismissedPayloadSchema = z.strictObject({
5660
+ events: z.array(z.string().regex(TRUST_EVENT_HASH_PATTERN)).min(1).max(16),
5661
+ issued_at: utcDatetime$1
5662
+ });
5663
+ /** The pinned root of a product, kept locally outside the repository (written by `pair`). */
5664
+ const RootPinSchema = z.strictObject({
5665
+ device: z.string().regex(/^(machine|human):[A-Za-z0-9_-]{16}$/),
5666
+ public_key: publicKey32
5667
+ });
5668
+ /**
5669
+ * What the authorization of a `device.paired` signs: its payload without `authorization`, plus the
5670
+ * label. Independent of the product and of `prev`: the bridge writes it in each journal it serves,
5671
+ * but cannot make one without the signer.
5672
+ */
5673
+ function devicePairedRecord(payload) {
5674
+ return {
5675
+ t: TRUST_SIGNATURE_TAGS.devicePaired,
5676
+ device: payload.device,
5677
+ kind: payload.kind,
5678
+ sig: payload.sig,
5679
+ enc: payload.enc,
5680
+ bridge: payload.bridge,
5681
+ provisional: payload.provisional,
5682
+ issued_at: payload.issued_at
5683
+ };
5684
+ }
5685
+ /** What the signed order `pair.offer` covers. */
5686
+ function pairOfferRecord(input) {
5687
+ return {
5688
+ t: TRUST_SIGNATURE_TAGS.pairOffer,
5689
+ kind: input.kind,
5690
+ bridge: input.bridge,
5691
+ x: input.order.x,
5692
+ expires_at: input.order.expires_at
5693
+ };
5694
+ }
5695
+ /** What the authorization of a `device.revoked` signs. */
5696
+ function deviceRevokedRecord(payload) {
5697
+ return {
5698
+ t: TRUST_SIGNATURE_TAGS.deviceRevoked,
5699
+ device: payload.device,
5700
+ chain_head: payload.chain_head,
5701
+ issued_at: payload.issued_at,
5702
+ author: payload.author
5703
+ };
5704
+ }
5705
+ /** What the authorization of a `device.root_certified` signs. */
5706
+ function rootCertifiedRecord(payload) {
5707
+ return {
5708
+ t: TRUST_SIGNATURE_TAGS.rootCertified,
5709
+ root: payload.root,
5710
+ issued_at: payload.issued_at
5711
+ };
5712
+ }
5713
+ //#endregion
5714
+ //#region ../../packages/protocol/dist/checkpoint-web.js
5715
+ /**
5716
+ * Event types of the answers given from the web view, at version 1. A reversible point's answer
5717
+ * is first held (`held`) for the length of the undo window, with the point still open for everyone;
5718
+ * then it is either taken back (`holdCancelled`) or carried out (`answered`). An answer to a point
5719
+ * that cannot be undone is carried out at once. A held answer whose carrying out the machine could
5720
+ * not do (the merge of a story that the forge refuses) is released (`holdFailed`): the point stays
5721
+ * open and says why.
5722
+ */
5723
+ const WEB_ANSWER_EVENT_TYPES = {
5724
+ answered: "checkpoint.web_answered",
5725
+ held: "checkpoint.web_held",
5726
+ holdCancelled: "checkpoint.web_hold_cancelled",
5727
+ holdFailed: "checkpoint.web_hold_failed"
5728
+ };
5729
+ /** How long the machine holds a reversible answer before it carries it out: the creator may undo it. */
5730
+ const WEB_ANSWER_WINDOW_MS = 1e4;
5731
+ /**
5732
+ * How long after its window a held answer is still waited for. Past it, the machine did not carry
5733
+ * the answer out (it is off, or the journal refuses it): the point is open again for a new answer.
5734
+ */
5735
+ const WEB_ANSWER_STALE_MS = 15e3;
5736
+ /** The longest note the creator types with a decision, like a resolution's note. */
5737
+ const MAX_WEB_ANSWER_NOTE_LENGTH = 2e3;
5738
+ /** Where an answer carried by a signed order came from: a browser (`web`) or a phone (`mobile`). */
5739
+ const ANSWER_SOURCES = ["web", "mobile"];
5740
+ /**
5741
+ * The source of an answer given by `device`, drawn from the kind of its identifier (a browser's
5742
+ * `browser:`, a phone's `phone:`), which the journal registered when the device was paired. Undefined
5743
+ * for any other device: a machine never answers this way.
5744
+ */
5745
+ function answerSourceOf(device) {
5746
+ if (device.startsWith("browser:")) return "web";
5747
+ if (device.startsWith("phone:")) return "mobile";
5748
+ }
5749
+ const ShortIdSchema$2 = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$/, { message: "expected 1 to 32 letters, digits, dots, hyphens or underscores" });
5750
+ function checkAnswer(value, context) {
5751
+ if (value.decision === "option_chosen" !== (value.option !== void 0)) context.addIssue({
5752
+ code: "custom",
5753
+ path: ["option"],
5754
+ message: "an option goes with option_chosen, and only with it"
5755
+ });
5756
+ if (NOTE_REQUIRED.has(value.decision) && value.note === void 0) context.addIssue({
5757
+ code: "custom",
5758
+ path: ["note"],
5759
+ message: "this decision needs a note"
5760
+ });
5761
+ }
5762
+ z.strictObject({
5763
+ checkpoint: CheckpointIdSchema,
5764
+ decision: z.enum(CHAT_DECISIONS),
5765
+ option: ShortIdSchema$2.optional(),
5766
+ note: z.string().trim().min(1).max(MAX_WEB_ANSWER_NOTE_LENGTH).optional()
5767
+ }).superRefine(checkAnswer);
5768
+ /** Refuses a source that is not the one the device's kind gives. */
5769
+ function checkSource(value, context) {
5770
+ if (answerSourceOf(value.device) !== value.source) context.addIssue({
5771
+ code: "custom",
5772
+ path: ["source"],
5773
+ message: "the source is the one the device's kind gives"
5774
+ });
5775
+ }
5776
+ /**
5777
+ * `payload` of `checkpoint.web_answered`: the point, the decision, the order that carried it and
5778
+ * the browser or phone device that signed it. The assurance (`device`) is drawn from the event's
5779
+ * type by the projection, never declared; the source must be the one the device's kind gives.
5780
+ */
5781
+ const CheckpointWebAnsweredPayloadSchema = z.strictObject({
5782
+ checkpoint: CheckpointIdSchema,
5783
+ decision: z.enum(CHAT_DECISIONS),
5784
+ option: ShortIdSchema$2.optional(),
5785
+ note: z.string().trim().min(1).max(MAX_WEB_ANSWER_NOTE_LENGTH).optional(),
5786
+ source: z.enum(ANSWER_SOURCES),
5787
+ order: UlidSchema,
5788
+ device: z.string().regex(PAIRED_DEVICE_ID_PATTERN)
5789
+ }).superRefine(checkAnswer).superRefine(checkSource);
5790
+ z.strictObject({ checkpoint: CheckpointIdSchema });
5791
+ /**
5792
+ * `payload` of `checkpoint.web_held`: an answer the machine holds for `WEB_ANSWER_WINDOW_MS`. It
5793
+ * is the answer of `checkpoint.web_answered`, which the machine writes when the window is over; the
5794
+ * point stays open until then.
5795
+ */
5796
+ const CheckpointWebHeldPayloadSchema = CheckpointWebAnsweredPayloadSchema;
5797
+ /**
5798
+ * `payload` of `checkpoint.web_hold_cancelled`: the held answer of the order `held` taken back by
5799
+ * the order `order`, signed by the browser device `device`. The point was never answered.
5800
+ */
5801
+ const CheckpointWebHoldCancelledPayloadSchema = z.strictObject({
5802
+ checkpoint: CheckpointIdSchema,
5803
+ held: UlidSchema,
5804
+ order: UlidSchema,
5805
+ device: z.string().regex(PAIRED_DEVICE_ID_PATTERN)
5806
+ });
5807
+ /** The longest code and reason a failed carrying out records: short tokens, never a message. */
5808
+ const FAILURE_TOKEN = /^[A-Za-z][A-Za-z0-9_.]{0,47}$/;
5809
+ /**
5810
+ * `payload` of `checkpoint.web_hold_failed`: the held answer of the order `held` that the machine
5811
+ * could not carry out (`code` is the engine's error code, `reason` its short reason when it has
5812
+ * one, `merge_failed` and `head_moved` for instance). Nothing was decided and the point is open
5813
+ * again. Neither a message nor an address nor a token is recorded.
5814
+ */
5815
+ const CheckpointWebHoldFailedPayloadSchema = z.strictObject({
5816
+ checkpoint: CheckpointIdSchema,
5817
+ held: UlidSchema,
5818
+ device: z.string().regex(PAIRED_DEVICE_ID_PATTERN),
5819
+ code: z.string().regex(FAILURE_TOKEN),
5820
+ reason: z.string().regex(FAILURE_TOKEN).optional()
5821
+ });
5822
+ //#endregion
5823
+ //#region ../../packages/protocol/dist/push.js
5824
+ /**
5825
+ * Notifications without content. A notification carries an opaque identifier, a category and a
5826
+ * level of urgency, and nothing else: the relay never learns a product's name, a title or a text.
5827
+ * The machine decides what is urgent (it knows the content); the relay only forwards, to the
5828
+ * phones of the account, through the push services of the platforms.
5829
+ */
5830
+ /** The platforms a phone can be reached on: APNs (iOS) and FCM (Android), directly. */
5831
+ const PUSH_PLATFORMS = ["ios", "android"];
5832
+ /** What a notification is about, in the words of the product (never a product's own text). */
5833
+ const NOTIFY_CATEGORIES = [
5834
+ "decision",
5835
+ "permission",
5836
+ "merge"
5837
+ ];
5838
+ /** How hard a notification interrupts. */
5839
+ const NOTIFY_URGENCIES = ["urgent", "normal"];
5840
+ const PushTokenSchema = z.string().min(16).max(4096).regex(/^[\x21-\x7e]+$/);
5841
+ /** The opaque identifier of a notification: 43 characters of base64url (a digest). */
5842
+ const NotifyIdSchema = z.string().regex(/^[A-Za-z0-9_-]{43}$/);
5843
+ /**
5844
+ * What a notification carries, in total. The relay builds the platform message from this and from
5845
+ * a fixed text of its own; nothing of the product is in it.
5846
+ */
5847
+ const PushNoticeSchema = z.strictObject({
5848
+ id: NotifyIdSchema,
5849
+ category: z.enum(NOTIFY_CATEGORIES),
5850
+ urgency: z.enum(NOTIFY_URGENCIES)
5851
+ });
5852
+ /** The surfaces of the creator that can be notified (the watch comes with its own app). */
5853
+ const NOTIFY_SURFACES = ["web", "mobile"];
5854
+ /** What a surface rings for: only the urgent, everything that comes back to the creator, nothing. */
5855
+ const SURFACE_CHOICES = [
5856
+ "urgent",
5857
+ "all",
5858
+ "none"
5859
+ ];
5860
+ /** A time zone name as the platforms give it (`Europe/Paris`): bounded, no free text. */
5861
+ const TimeZoneSchema = z.string().regex(/^[A-Za-z][A-Za-z0-9_+\-/]{0,63}$/);
5862
+ const CategoriesSchema = z.array(z.enum(NOTIFY_CATEGORIES)).max(NOTIFY_CATEGORIES.length).refine((list) => new Set(list).size === list.length, { message: "duplicate category" });
5863
+ const MinuteOfDaySchema = z.number().int().min(0).max(1439);
5864
+ /**
5865
+ * What the creator chose about notifications. It holds no product text and no content: categories,
5866
+ * hours and surfaces. The relay keeps it in clear because it applies it (it never sees more than an
5867
+ * opaque notice); the same document is shown and changed from every surface.
5868
+ */
5869
+ const NotifyPrefsSchema = z.strictObject({
5870
+ /** The categories that notify at all. */
5871
+ types: CategoriesSchema,
5872
+ /** Active on one surface: the others stay quiet. Off: each surface follows its own choice. */
5873
+ presence: z.boolean(),
5874
+ surfaces: z.strictObject({
5875
+ web: z.enum(SURFACE_CHOICES),
5876
+ mobile: z.enum(SURFACE_CHOICES)
5877
+ }),
5878
+ quiet: z.strictObject({
5879
+ enabled: z.boolean(),
5880
+ /** Minute of the day the silence starts and ends, in `tz`. Equal values mean no silence. */
5881
+ from: MinuteOfDaySchema,
5882
+ to: MinuteOfDaySchema,
5883
+ tz: TimeZoneSchema,
5884
+ /** The urgent categories that still ring during the silence: none by default. */
5885
+ pass: CategoriesSchema
5886
+ })
5887
+ });
5888
+ [...NOTIFY_CATEGORIES];
5889
+ /** The two briefings: the morning one (what waits) and the evening one (the day, then the calm decisions). */
5890
+ const BRIEFING_PARTS = ["morning", "evening"];
5891
+ /** The request shape (without the identifier, the version and the product, which the file reading adds). */
5892
+ const BriefingRequestFields = {
5893
+ op: z.literal("briefing"),
5894
+ /** Absent: the morning briefing. */
5895
+ part: z.enum(BRIEFING_PARTS).optional()
5896
+ };
5897
+ const Count$2 = z.int().min(0).max(Number.MAX_SAFE_INTEGER);
5898
+ /** What the creator can do with a point from a phone: every decision, none to approve, or none at all. */
5899
+ const BRIEFING_ANSWERING = [
5900
+ "full",
5901
+ "no_approval",
5902
+ "elsewhere"
5903
+ ];
5904
+ const BriefingOptionSchema = z.strictObject({
5905
+ id: z.string().min(1).max(64),
5906
+ label: z.string().min(1).max(100),
5907
+ recommended: z.boolean()
5908
+ });
5909
+ /**
5910
+ * What the listening version of a point is made from: the fields its agent already wrote when it
5911
+ * opened the point (the card's effect, its first risk and its reason, a deliverable's short
5912
+ * version), as plain text, or the start of the summary when no card was written. The device turns
5913
+ * them into sentences of its own catalog; the machine never writes a sentence, and nothing is
5914
+ * asked of a model. Every field is untrusted text written by an agent.
5915
+ */
5916
+ const BriefingListenSchema = z.strictObject({
5917
+ /** What the point changes for the people who use the product (the card's `effect`). */
5918
+ effect: z.string().min(1).max(300).optional(),
5919
+ /** The start of the summary: only when the agent wrote no card. */
5920
+ summary: z.string().min(1).max(600).optional(),
5921
+ /** The first risk of the card. */
5922
+ risk: z.string().min(1).max(200).optional(),
5923
+ /** Why the agent proposes this, and the alternative it set aside. */
5924
+ reason: z.string().min(1).max(200).optional(),
5925
+ rejected: z.string().min(1).max(200).optional(),
5926
+ /** A deliverable's short version, written by its agent for about fifteen seconds of reading. */
5927
+ short: z.string().min(1).max(600).optional()
5928
+ });
5929
+ const BriefingItemSchema = z.strictObject({
5930
+ /** The checkpoint's identifier: the device finds the point on its own board with it. */
5931
+ id: z.string().min(1).max(64),
5932
+ type: z.string().min(1).max(32),
5933
+ risk: z.enum([
5934
+ "low",
5935
+ "medium",
5936
+ "high"
5937
+ ]),
5938
+ /** True when the point holds something back or sits at a high risk (the rule of the notifications). */
5939
+ urgent: z.boolean(),
5940
+ /** True when the point cannot be taken back once decided. */
5941
+ irreversible: z.boolean(),
5942
+ answering: z.enum(BRIEFING_ANSWERING),
5943
+ /** The agent that opened the point, when one did. */
5944
+ agent: z.string().min(1).max(64).optional(),
5945
+ title: z.string().min(1).max(120),
5946
+ story: z.string().min(1).max(32).optional(),
5947
+ options: z.array(BriefingOptionSchema).max(6),
5948
+ /** The fields the listening version is made from; absent when the point has no text to read. */
5949
+ listen: BriefingListenSchema.optional(),
5950
+ /** Set on a story's merge point: the story and the branch it lands on. */
5951
+ merge: z.strictObject({
5952
+ story: z.string().min(1).max(32),
5953
+ base: z.string().min(1).max(100)
5954
+ }).optional()
5955
+ });
5956
+ /**
5957
+ * The day of a product, in the evening briefing: what was delivered since midnight on the machine's
5958
+ * clock, how many points still wait at a high risk or holding something back (they are counted,
5959
+ * never listed: they are not for the evening), and what the day cost when the bridge could tell.
5960
+ */
5961
+ const BriefingDaySchema = z.strictObject({
5962
+ /** Stories merged today. */
5963
+ merged: Count$2,
5964
+ /** Publications the creator validated today. */
5965
+ published: Count$2,
5966
+ /** Decisions the creator made today. */
5967
+ decided: Count$2,
5968
+ /** Points that wait and are urgent: not in `items`. */
5969
+ urgent: Count$2,
5970
+ /** What the measured sessions of the day cost; absent when the costs cannot be read. */
5971
+ cost: z.strictObject({
5972
+ /** Dollars of the models the table prices; absent when no model of the day has a price. */
5973
+ usd: z.number().min(0).max(1e8).optional(),
5974
+ /** `real`, `partial` or `unknown`: a device shows another value as `unknown`. */
5975
+ nature: z.string().min(1).max(16),
5976
+ tokens: Count$2
5977
+ }).optional()
5978
+ });
5979
+ const BriefingDataSchema = z.strictObject({
5980
+ op: z.literal("briefing"),
5981
+ /** Which briefing this is; absent in a morning briefing. */
5982
+ part: z.enum(BRIEFING_PARTS).optional(),
5983
+ /** The day of the product; present in an evening briefing only. */
5984
+ day: BriefingDaySchema.optional(),
5985
+ /** Milliseconds since the epoch, on the machine's clock. */
5986
+ composed_at: Count$2,
5987
+ /** What moved in the last 24 hours (in the evening briefing, since midnight). */
5988
+ progress: z.strictObject({
5989
+ merged: Count$2,
5990
+ decided: Count$2
5991
+ }),
5992
+ /** How many points wait for the creator (held answers excluded), whatever `items` lists. */
5993
+ waiting: Count$2,
5994
+ items: z.array(BriefingItemSchema).max(12)
5995
+ });
5996
+ //#endregion
5997
+ //#region ../../packages/protocol/dist/budget.js
5998
+ /**
5999
+ * Budgets: each story's ceiling comes from the journal (the last sprint approved that
6000
+ * holds it, or the creator's answer to a budget checkpoint); its cost from the agents' declared
6001
+ * costs (`cost.reported`, encrypted). When a declared cost reaches the alert ratio, or goes over
6002
+ * the ceiling, `core` writes `budget.threshold_reached` (no amount of cost in it), right after
6003
+ * that cost in the same write. Over the ceiling, with `on_exceed: block`, it opens a budget
6004
+ * checkpoint in between, and the story's write tools answer `BUDGET_EXCEEDED` until the creator
6005
+ * answers it.
6006
+ */
6007
+ /** Event types of budgets, each at version 1. */
6008
+ const BUDGET_EVENT_TYPES = { thresholdReached: "budget.threshold_reached" };
6009
+ /** The thresholds a story's cost may reach: the alert ratio, then the ceiling itself. */
6010
+ const BUDGET_LEVELS = ["alert", "exceeded"];
6011
+ /** The largest ceiling, in US dollars (raising a ceiling stops there). */
6012
+ const MAX_BUDGET_USD = 1e9;
6013
+ /**
6014
+ * The options of a budget checkpoint: raise the ceiling by half (at least to $1), double it (at
6015
+ * least to $2), go on without a ceiling, or stop the story. A raise that would change nothing (at
6016
+ * `MAX_BUDGET_USD`) is never offered.
6017
+ */
6018
+ const BUDGET_OPTIONS = [
6019
+ "raise-50",
6020
+ "raise-100",
6021
+ "no-limit",
6022
+ "stop"
6023
+ ];
6024
+ /** The option a budget checkpoint recommends when it offers it (else `stop`). */
6025
+ const BUDGET_RECOMMENDATION = "raise-50";
6026
+ /** The prefix of a budget checkpoint's title, before the story. */
6027
+ const BUDGET_TITLE_PREFIX = "Budget exceeded: ";
6028
+ /**
6029
+ * The source of a ceiling: the sprint (`S-…`) whose approval set it, or the budget checkpoint
6030
+ * (`G-…`) whose answer did. A threshold counts only for the ceiling it names.
6031
+ */
6032
+ const CeilingIdSchema = z.string().regex(/^[SG]-[0-9A-HJKMNP-TV-Z]{10}$/, { message: "expected the id of a sprint (S-…) or of a checkpoint (G-…)" });
6033
+ /**
6034
+ * `payload` of `budget.threshold_reached`: the level reached, the ceiling it was measured against
6035
+ * (its amount and its source, never the cost), the settings in force, and the budget checkpoint
6036
+ * opened just before it in the same write (`exceeded` under `block` only; absent when one is
6037
+ * already open).
6038
+ */
6039
+ const BudgetThresholdReachedPayloadSchema = z.strictObject({
6040
+ level: z.enum(BUDGET_LEVELS),
6041
+ budget_usd: z.number().finite().min(0).max(MAX_BUDGET_USD),
6042
+ ceiling: CeilingIdSchema,
6043
+ alert_ratio: z.number().finite().min(MIN_ALERT_RATIO).max(1),
6044
+ on_exceed: z.enum(BUDGET_EXCEED_MODES),
6045
+ checkpoint: CheckpointIdSchema.optional()
6046
+ }).superRefine((value, context) => {
6047
+ if (value.checkpoint !== void 0 && (value.level !== "exceeded" || value.on_exceed !== "block")) context.addIssue({
6048
+ code: "custom",
6049
+ path: ["checkpoint"],
6050
+ message: "a checkpoint goes only with an exceeded level under block"
6051
+ });
6052
+ });
6053
+ //#endregion
6054
+ //#region ../../packages/protocol/dist/call-guard.js
6055
+ /**
6056
+ * The guard before every tool call. A hook of the AI tool runs before each tool (`check_call`);
6057
+ * the engine refuses the call when the story it belongs to reached its budget, or when the team is
6058
+ * paused and the call would write, run a command or start an agent, and lets it through otherwise.
6059
+ * Values come as the hook substitutes them (`${tool_name}`): possibly empty, or left as `${…}` when
6060
+ * the hook's input has no such field; the engine reads them with that doubt. In doubt the call goes
6061
+ * through.
6062
+ */
6063
+ const CheckCallInputSchema = z.strictObject({
6064
+ /** The tool about to run (`Bash`, `Edit`, `mcp__<server>__<tool>`…). */
6065
+ tool: z.string().max(200),
6066
+ session: z.string().trim().min(1).max(200).optional(),
6067
+ cwd: z.string().max(2e3).optional(),
6068
+ /** The kind of sub-agent that makes the call, when the tool says it (absent for the main session). */
6069
+ agent: z.string().max(200).optional()
6070
+ });
6071
+ z.record(z.string(), z.unknown());
6072
+ /** The event Noah writes when he takes a message of the creator. */
6073
+ const CHAIN_EVENT_TYPES = { instructionGiven: "agent.instruction_given" };
6074
+ /** The longest message, in characters. */
6075
+ const CHAIN_INSTRUCTION_MAX_CHARS = 4e3;
6076
+ const AgentTargetSchema = AgentIdSchema.max(64);
6077
+ /**
6078
+ * Fields that devices older than this version still send with a pause or a resumption. They are
6079
+ * read and ignored, so that an older phone keeps its buttons; no view sends them any more.
6080
+ */
6081
+ const IGNORED_OLD_FIELDS = {
6082
+ when: z.enum(["now", "after_step"]).optional(),
6083
+ hand: z.enum(["chat", "terminal"]).optional(),
6084
+ close_session: z.boolean().optional()
6085
+ };
6086
+ z.strictObject({
6087
+ agent: AgentTargetSchema.optional(),
6088
+ ...IGNORED_OLD_FIELDS
6089
+ });
6090
+ z.strictObject({
6091
+ agent: AgentTargetSchema.optional(),
6092
+ ...IGNORED_OLD_FIELDS
6093
+ });
6094
+ z.strictObject({ agent: AgentTargetSchema.optional() });
6095
+ z.strictObject({
6096
+ text: z.string().min(1).max(CHAIN_INSTRUCTION_MAX_CHARS).refine((value) => value.trim().length > 0, { message: "the message is empty" }),
6097
+ input: z.enum(["text", "voice"]).optional()
6098
+ });
6099
+ /** `payload` of `agent.instruction_given`: the creator's message, kept encrypted in the journal. */
6100
+ const InstructionGivenPayloadSchema = z.strictObject({
6101
+ /** Who the creator spoke to. */
6102
+ to: AgentTargetSchema,
6103
+ /** The agent the message went to when Noah routed it (events of earlier versions). */
6104
+ routed_to: AgentTargetSchema.optional(),
6105
+ text: z.string().min(1).max(CHAIN_INSTRUCTION_MAX_CHARS),
6106
+ /** How the message was delivered (events of earlier versions use `now` and `queued`). */
6107
+ delivery: z.enum([
6108
+ "now",
6109
+ "queued",
6110
+ "chat"
6111
+ ])
6112
+ });
6113
+ //#endregion
6114
+ //#region ../../packages/protocol/dist/checkpoint-closed.js
6115
+ /**
6116
+ * A point closed by the engine itself: when what a checkpoint asks about is already
6117
+ * settled, nobody has to answer it. The engine writes `checkpoint.closed` with the reason, and the
6118
+ * projection checks the reason against the journal (a point that is still waiting for something is
6119
+ * never closed). The creator is never shown a point whose object is settled.
6120
+ */
6121
+ /** Event type of the closing of a checkpoint by the engine, at version 1. */
6122
+ const CHECKPOINT_CLOSED_EVENT_TYPES = { closed: "checkpoint.closed" };
6123
+ /** `payload` of `checkpoint.closed`: the checkpoint, why, and the newer one for `replaced`. */
6124
+ const CheckpointClosedPayloadSchema = z.strictObject({
6125
+ checkpoint: CheckpointIdSchema,
6126
+ reason: z.enum([
6127
+ "story_merged",
6128
+ "review_redone",
6129
+ "replaced",
6130
+ "step_skipped",
6131
+ "block_lifted",
6132
+ "worktree_gone",
6133
+ "lapsed"
6134
+ ]),
6135
+ /** The checkpoint that replaces it: with `replaced`, and only with it. */
6136
+ ref: CheckpointIdSchema.optional()
6137
+ }).refine((payload) => payload.reason === "replaced" === (payload.ref !== void 0), {
6138
+ message: "a ref goes with replaced, and only with it",
6139
+ path: ["ref"]
6140
+ });
6141
+ //#endregion
6142
+ //#region ../../packages/protocol/dist/checkpoint-decided.js
6143
+ /**
6144
+ * A point decided by the agent responsible for it. The creator keeps for
6145
+ * themselves the points they chose (`validation.points` of `method.yaml`); any other point is
6146
+ * decided by an agent, which says why. The decision is written as the agent (`actor.kind: agent`,
6147
+ * signed by the machine key), never as the creator: its source and its assurance are `agent`, so
6148
+ * the history, the morning summary and every surface always tell it apart from an answer of the
6149
+ * creator, and the creator can reopen it at any time.
6150
+ */
6151
+ /** Event type of a point decided by an agent, at version 1. */
6152
+ const CHECKPOINT_DECIDED_EVENT_TYPES = { decided: "checkpoint.decided" };
6153
+ /**
6154
+ * What `decide_checkpoint` may be given: a decision, or `escalate`, the agent handing the point back
6155
+ * to the creator with its reason when it cannot or should not decide it (a merge the forge refuses
6156
+ * and nobody can fix, a point it finds beyond its remit). The point then waits for the creator.
6157
+ */
6158
+ const DECIDE_CHOICES = [...CHAT_DECISIONS, "escalate"];
6159
+ const ShortIdSchema$1 = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$/, { message: "expected 1 to 32 letters, digits, dots, hyphens or underscores" });
6160
+ const ReasonSchema$1 = z.string().trim().min(1).max(1e3);
6161
+ function checkOption(value, context) {
6162
+ if (value.decision === "option_chosen" !== (value.option !== void 0)) context.addIssue({
6163
+ code: "custom",
6164
+ path: ["option"],
6165
+ message: "an option goes with option_chosen, and only with it"
6166
+ });
6167
+ }
6168
+ /**
6169
+ * Input of `decide_checkpoint`: the agent that decides, the point, its decision and the reason.
6170
+ * `option` goes with `option_chosen` only.
6171
+ */
6172
+ const DecideCheckpointInputSchema = z.strictObject({
6173
+ agent: AgentIdSchema.max(64),
6174
+ checkpoint: z.string().trim().toUpperCase().pipe(CheckpointIdSchema),
6175
+ decision: z.enum(DECIDE_CHOICES),
6176
+ option: ShortIdSchema$1.optional(),
6177
+ reason: ReasonSchema$1
6178
+ }).superRefine(checkOption);
6179
+ /** `payload` of `checkpoint.decided`: the point, the decision and the agent's reason. */
5285
6180
  const CheckpointDecidedPayloadSchema = z.strictObject({
5286
6181
  checkpoint: CheckpointIdSchema,
5287
6182
  decision: z.enum(DECIDE_CHOICES),
@@ -5357,6 +6252,364 @@ const CheckCiInputSchema = z.strictObject({
5357
6252
  forge: z.enum(CI_FORGES).optional()
5358
6253
  });
5359
6254
  //#endregion
6255
+ //#region ../../packages/protocol/dist/forge-link.js
6256
+ /**
6257
+ * Linking a code platform (GitHub, GitLab) to a machine. The creator's device asks, with a signed
6258
+ * order, for the machine to be authorised at the platform; the machine does it itself, keeps the
6259
+ * token in its own system keychain and says what became of it in its report. The token is in none
6260
+ * of these shapes: a device only ever learns the state of a link (who, how far, until when) and,
6261
+ * while an authorisation is under way, the short code and the public address to enter it at.
6262
+ */
6263
+ /** The platforms a machine can be linked to. */
6264
+ const FORGE_PROVIDERS = ["github", "gitlab"];
6265
+ const ForgeHostSchema = z.string().max(50).regex(/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*(?::[1-9][0-9]{0,4})?$/).refine((host) => {
6266
+ const port = host.split(":")[1];
6267
+ return port === void 0 || Number(port) <= 65535;
6268
+ });
6269
+ z.strictObject({
6270
+ provider: z.enum(FORGE_PROVIDERS),
6271
+ host: ForgeHostSchema.optional()
6272
+ });
6273
+ const ForgeLinkStatusSchema = z.strictObject({
6274
+ provider: z.enum(FORGE_PROVIDERS),
6275
+ host: ForgeHostSchema,
6276
+ state: z.enum([
6277
+ "pending",
6278
+ "linked",
6279
+ "relink",
6280
+ "failed"
6281
+ ]),
6282
+ /** How the token was obtained: the platform's device flow, or given on the machine. */
6283
+ method: z.enum(["device", "token"]).optional(),
6284
+ /** The account the token belongs to (the platform's login). */
6285
+ account: z.string().min(1).max(100).optional(),
6286
+ access: z.enum([
6287
+ "read",
6288
+ "write",
6289
+ "unknown"
6290
+ ]).optional(),
6291
+ /** The scopes or permissions the platform reported, as it names them. */
6292
+ scopes: z.array(z.string().min(1).max(60)).max(12).optional(),
6293
+ repos: z.discriminatedUnion("kind", [z.strictObject({ kind: z.literal("all") }), z.strictObject({
6294
+ kind: z.literal("listed"),
6295
+ names: z.array(z.string().min(1).max(140)).max(30),
6296
+ more: z.boolean()
6297
+ })]).optional(),
6298
+ linked_at: z.iso.datetime().optional(),
6299
+ /** When the token stops working; absent when the platform did not say. */
6300
+ expires_at: z.iso.datetime().optional(),
6301
+ pending: z.strictObject({
6302
+ user_code: z.string().min(1).max(32),
6303
+ /** The public address to enter the code at. */
6304
+ verification_uri: z.string().max(300),
6305
+ expires_at: z.iso.datetime()
6306
+ }).optional(),
6307
+ failure: z.enum([
6308
+ "expired",
6309
+ "denied",
6310
+ "unreachable",
6311
+ "rejected",
6312
+ "not_saved"
6313
+ ]).optional()
6314
+ });
6315
+ const CODE_PATCH_MAX_CHARS = 4e4;
6316
+ const CODE_TEXT_MAX_CHARS = 2e3;
6317
+ /** The number of a pull request (GitHub) or merge request (GitLab) in its repository. */
6318
+ const CodePullNumberSchema = z.int().min(1).max(1e9);
6319
+ const CODE_PULL_STATES = [
6320
+ "open",
6321
+ "draft",
6322
+ "merged",
6323
+ "closed"
6324
+ ];
6325
+ /** Where the checks of a pull request stand; `unknown` when the bridge did not read them. */
6326
+ const CODE_CHECKS_STATES = [
6327
+ "passed",
6328
+ "failed",
6329
+ "running",
6330
+ "none",
6331
+ "unknown"
6332
+ ];
6333
+ /** Where the review of a pull request stands; `unknown` when the bridge did not read it. */
6334
+ const CODE_REVIEW_STATES = [
6335
+ "approved",
6336
+ "changes_requested",
6337
+ "pending",
6338
+ "none",
6339
+ "unknown"
6340
+ ];
6341
+ /** The repository the pull requests are read from, as the machine knows it. */
6342
+ const CodeRepositorySchema = z.strictObject({
6343
+ provider: z.enum(FORGE_PROVIDERS),
6344
+ host: ForgeHostSchema,
6345
+ /** `owner/name` (GitLab: the group path, possibly nested). */
6346
+ path: z.string().min(3).max(200)
6347
+ });
6348
+ const CodeChecksSummarySchema = z.strictObject({
6349
+ state: z.enum(CODE_CHECKS_STATES),
6350
+ passed: z.int().min(0).max(1e4),
6351
+ failed: z.int().min(0).max(1e4),
6352
+ total: z.int().min(0).max(1e4)
6353
+ });
6354
+ const CodePullSchema = z.strictObject({
6355
+ number: CodePullNumberSchema,
6356
+ title: z.string().max(200),
6357
+ state: z.enum(CODE_PULL_STATES),
6358
+ /** The platform's login of the author. */
6359
+ author: z.string().min(1).max(100).optional(),
6360
+ /** The branch the work is on, and the branch it aims at. */
6361
+ branch: z.string().max(200),
6362
+ base: z.string().max(200),
6363
+ /** The address of the pull request at the platform, built by the bridge. */
6364
+ url: z.string().max(400),
6365
+ created_at: z.iso.datetime(),
6366
+ updated_at: z.iso.datetime(),
6367
+ checks: CodeChecksSummarySchema,
6368
+ review: z.enum(CODE_REVIEW_STATES),
6369
+ additions: z.int().min(0).optional(),
6370
+ deletions: z.int().min(0).optional(),
6371
+ files: z.int().min(0).optional()
6372
+ });
6373
+ const CodeCheckSchema = z.strictObject({
6374
+ name: z.string().min(1).max(120),
6375
+ state: z.enum([
6376
+ "passed",
6377
+ "failed",
6378
+ "running",
6379
+ "queued",
6380
+ "skipped"
6381
+ ]),
6382
+ /** How long the step took, in seconds, when the platform says. */
6383
+ seconds: z.int().min(0).max(1e6).optional()
6384
+ });
6385
+ const CodeReviewSchema = z.strictObject({
6386
+ author: z.string().min(1).max(100).optional(),
6387
+ verdict: z.enum([
6388
+ "approved",
6389
+ "changes_requested",
6390
+ "commented",
6391
+ "dismissed"
6392
+ ]),
6393
+ body: z.string().max(CODE_TEXT_MAX_CHARS).optional(),
6394
+ at: z.iso.datetime().optional()
6395
+ });
6396
+ /** A remark anchored to a line of a file of the pull request. */
6397
+ const CodeRemarkSchema = z.strictObject({
6398
+ id: z.string().min(1).max(40),
6399
+ author: z.string().min(1).max(100).optional(),
6400
+ path: z.string().min(1).max(1024),
6401
+ /** The line of the file: after the change (`new`) or before it (`old`, a removed line). */
6402
+ line: z.int().min(1).max(1e7),
6403
+ side: z.enum(["new", "old"]),
6404
+ body: z.string().max(CODE_TEXT_MAX_CHARS),
6405
+ at: z.iso.datetime().optional()
6406
+ });
6407
+ /**
6408
+ * One file of a difference. `patch` is the platform's unified difference without its header (it
6409
+ * starts at the first `@@` line), each line that looked like a secret replaced by the fixed
6410
+ * notice; absent for a binary or a very large file.
6411
+ */
6412
+ const CodeDiffFileSchema = z.strictObject({
6413
+ path: z.string().min(1).max(1024),
6414
+ from: z.string().min(1).max(1024).optional(),
6415
+ status: z.enum([
6416
+ "added",
6417
+ "modified",
6418
+ "deleted",
6419
+ "renamed"
6420
+ ]),
6421
+ additions: z.int().min(0),
6422
+ deletions: z.int().min(0),
6423
+ patch: z.string().max(CODE_PATCH_MAX_CHARS).optional(),
6424
+ /** The patch is cut at the longest the bridge sends. */
6425
+ truncated: z.literal(true).optional(),
6426
+ /** The platform shows no text for this file (binary, or too large for it). */
6427
+ unavailable: z.literal(true).optional(),
6428
+ /** The file is a secret's by its name: its difference is never sent. */
6429
+ protected: z.literal(true).optional()
6430
+ });
6431
+ /** The editors a link can open. The scheme of each is fixed on the device; nothing is run. */
6432
+ const CODE_EDITORS = [
6433
+ "vscode",
6434
+ "cursor",
6435
+ "windsurf",
6436
+ "vscodium",
6437
+ "zed",
6438
+ "idea",
6439
+ "webstorm",
6440
+ "pycharm"
6441
+ ];
6442
+ const CODE_PLATFORMS = [
6443
+ "windows",
6444
+ "macos",
6445
+ "linux",
6446
+ "other"
6447
+ ];
6448
+ /** The working copy an editor is opened on: the product's own folder, or a story's branch. */
6449
+ const CodeEditorRefSchema = z.discriminatedUnion("kind", [z.strictObject({ kind: z.literal("worktree") }), z.strictObject({
6450
+ kind: z.literal("workspace"),
6451
+ branch: z.string().min(1).max(200)
6452
+ })]);
6453
+ /** The request shapes (without the identifier, the version and the product, which the file reading adds). */
6454
+ const CodePullsRequestFields = { op: z.literal("pulls") };
6455
+ const CodePullRequestFields = {
6456
+ op: z.literal("pull"),
6457
+ number: CodePullNumberSchema
6458
+ };
6459
+ const CodeDiffRequestFields = {
6460
+ op: z.literal("pull_diff"),
6461
+ number: CodePullNumberSchema
6462
+ };
6463
+ const CodeEditorRequestFields = {
6464
+ op: z.literal("editor"),
6465
+ ref: CodeEditorRefSchema
6466
+ };
6467
+ /** What an answer carries. */
6468
+ const CodePullsDataSchema = z.strictObject({
6469
+ op: z.literal("pulls"),
6470
+ repository: CodeRepositorySchema,
6471
+ pulls: z.array(CodePullSchema).max(30),
6472
+ /** True when the platform has more than the bridge lists. */
6473
+ truncated: z.boolean()
6474
+ });
6475
+ const CodePullDataSchema = z.strictObject({
6476
+ op: z.literal("pull"),
6477
+ repository: CodeRepositorySchema,
6478
+ pull: CodePullSchema,
6479
+ checks: z.array(CodeCheckSchema).max(60),
6480
+ reviews: z.array(CodeReviewSchema).max(30),
6481
+ remarks: z.array(CodeRemarkSchema).max(100)
6482
+ });
6483
+ const CodeDiffDataSchema = z.strictObject({
6484
+ op: z.literal("pull_diff"),
6485
+ repository: CodeRepositorySchema,
6486
+ number: CodePullNumberSchema,
6487
+ files: z.array(CodeDiffFileSchema).max(100),
6488
+ /** True when the pull request changes more files than the bridge sends. */
6489
+ truncated: z.boolean()
6490
+ });
6491
+ const CodeEditorDataSchema = z.strictObject({
6492
+ op: z.literal("editor"),
6493
+ /** The folder of the working copy on the machine. */
6494
+ folder: z.string().min(1).max(1024),
6495
+ /** The branch checked out there, when it is on one. */
6496
+ branch: z.string().min(1).max(200).optional(),
6497
+ /** The system of the machine, so that a device of another system knows the link is not for it. */
6498
+ platform: z.enum(CODE_PLATFORMS),
6499
+ /** The editors found on the machine, best known first. */
6500
+ detected: z.array(z.enum(CODE_EDITORS)).max(CODE_EDITORS.length)
6501
+ });
6502
+ /** The stable codes of a refusal that only the code reading gives. */
6503
+ const CODE_ERROR_CODES = [
6504
+ "NOT_LINKED",
6505
+ "NO_REMOTE",
6506
+ "FORGE_REFUSED",
6507
+ "FORGE_UNREACHABLE",
6508
+ "FORGE_BUSY"
6509
+ ];
6510
+ /** The request shape (without the identifier, the version and the product, which the file reading adds). */
6511
+ const CostsRequestFields = {
6512
+ op: z.literal("costs"),
6513
+ period: z.enum([
6514
+ "today",
6515
+ "week",
6516
+ "sprint",
6517
+ "month",
6518
+ "all"
6519
+ ]).optional()
6520
+ };
6521
+ const Count$1 = z.int().min(0).max(Number.MAX_SAFE_INTEGER);
6522
+ /** What a set of measured parts consumed, and what it costs at the table's prices. */
6523
+ const CostFiguresSchema = z.strictObject({
6524
+ /** The dollars of the models the table prices; absent when `nature` is `unknown`. */
6525
+ usd: z.number().min(0).max(1e8).optional(),
6526
+ nature: z.string().min(1).max(16),
6527
+ tokens_in: Count$1,
6528
+ tokens_out: Count$1,
6529
+ cache_read: Count$1,
6530
+ cache_write: Count$1,
6531
+ /** Working time, in milliseconds. */
6532
+ active_ms: Count$1,
6533
+ /** The models that have no price in the table (their tokens count, their dollars do not). */
6534
+ unpriced: z.array(z.string().min(1).max(100)).max(8)
6535
+ });
6536
+ const CostWorkSchema = z.strictObject({
6537
+ kind: z.string().min(1).max(24),
6538
+ /** The file's name, the checkpoint, the story, ... that the work is about. */
6539
+ key: z.string().min(1).max(128),
6540
+ label: z.string().max(200).optional(),
6541
+ /** The agent that did it; `noah` for the creator's conversation with Noah. */
6542
+ agent: z.string().min(1).max(64),
6543
+ story: z.string().regex(/^[1-9]\d{0,5}-[1-9]\d{0,5}$/).optional(),
6544
+ figures: CostFiguresSchema
6545
+ });
6546
+ const CostPhaseSchema = z.strictObject({
6547
+ /** The phase of the method; absent for what ran when no phase was current. */
6548
+ phase: z.string().min(1).max(32).optional(),
6549
+ figures: CostFiguresSchema,
6550
+ works: z.array(CostWorkSchema).max(60),
6551
+ /** True when the phase holds more works than listed (the figures count them all). */
6552
+ truncated: z.boolean()
6553
+ });
6554
+ const CostAgentSchema = z.strictObject({
6555
+ agent: z.string().min(1).max(64),
6556
+ figures: CostFiguresSchema
6557
+ });
6558
+ const CostStorySchema = z.strictObject({
6559
+ story: z.string().regex(/^[1-9]\d{0,5}-[1-9]\d{0,5}$/),
6560
+ /** The sprint that carries it, when one does. */
6561
+ sprint: z.string().min(1).max(64).optional(),
6562
+ figures: CostFiguresSchema
6563
+ });
6564
+ const CostSprintSchema = z.strictObject({
6565
+ id: z.string().min(1).max(64),
6566
+ goal: z.string().max(200),
6567
+ /** Where the sprint stands (`in_force`, `superseded`, ...); a device shows an unknown one in neutral words. */
6568
+ status: z.string().min(1).max(24),
6569
+ stories: Count$1,
6570
+ /** The budget the sprint was planned with, in dollars. */
6571
+ budget_usd: z.number().min(0).max(1e8),
6572
+ figures: CostFiguresSchema
6573
+ });
6574
+ /** What the agents declared on their own, apart from the measure. */
6575
+ const CostDeclaredAgentSchema = z.strictObject({
6576
+ agent: z.string().min(1).max(64),
6577
+ /** Dollars declared as real, and as estimated. */
6578
+ usd: z.number().min(0).max(1e8),
6579
+ usd_estimated: z.number().min(0).max(1e8),
6580
+ tokens_in: Count$1,
6581
+ tokens_out: Count$1
6582
+ });
6583
+ const CostDeclaredSchema = z.strictObject({
6584
+ usd: z.number().min(0).max(1e8),
6585
+ usd_estimated: z.number().min(0).max(1e8),
6586
+ tokens_in: Count$1,
6587
+ tokens_out: Count$1,
6588
+ agents: z.array(CostDeclaredAgentSchema).max(60)
6589
+ });
6590
+ const CostsDataSchema = z.strictObject({
6591
+ op: z.literal("costs"),
6592
+ period: z.string().min(1).max(16),
6593
+ /** The price table the dollars were computed with. */
6594
+ table: z.strictObject({
6595
+ version: z.string().min(1).max(32),
6596
+ as_of: z.string().min(1).max(32)
6597
+ }),
6598
+ /** False when no session was measured in the period: no figure, never a zero. */
6599
+ measured: z.boolean(),
6600
+ /** How many measured sessions the figures come from. */
6601
+ sessions: Count$1,
6602
+ total: CostFiguresSchema,
6603
+ phases: z.array(CostPhaseSchema).max(12),
6604
+ agents: z.array(CostAgentSchema).max(60),
6605
+ stories: z.array(CostStorySchema).max(400),
6606
+ sprints: z.array(CostSprintSchema).max(60),
6607
+ /** Absent when no agent declared a cost (or the period is not `all`: declarations carry no date). */
6608
+ declared: CostDeclaredSchema.optional(),
6609
+ /** True when a list was cut at its limit (the totals count everything). */
6610
+ truncated: z.boolean()
6611
+ });
6612
+ //#endregion
5360
6613
  //#region ../../packages/protocol/dist/install.js
5361
6614
  /**
5362
6615
  * The installations the approved architecture plans. The architecture document
@@ -6010,216 +7263,303 @@ const AgentDelegatedPayloadSchema = z.strictObject({
6010
7263
  brief: BriefSchema
6011
7264
  });
6012
7265
  //#endregion
6013
- //#region ../../packages/protocol/dist/device-trust.js
7266
+ //#region ../../packages/protocol/dist/trace.js
6014
7267
  /**
6015
- * The devices of trust in the journal. A product's
6016
- * registry of trusted devices is its journal: `device.paired` says "this key is trusted",
6017
- * `device.revoked` takes it back, `device.confirmed` ends the provisional state of a device whose
6018
- * handover is complete, `device.root_certified` records that the first phone recognised the root,
6019
- * and `trust.dismissed` clears a `SECURITY_ROOT_CHANGED` alert. The chain of authorizations must
6020
- * lead to the root the bridge pinned locally, outside the repository: never to an event's ULID.
6021
- *
6022
- * Generic on purpose: schemas and rules only, no transport, no account, no offer.
7268
+ * Traceability and deviations. Each requirement of the PRD (`EF-n`, `ENF-n`) is linked,
7269
+ * at read time, to the stories that cite it, their pull requests, the tests that cite it and the
7270
+ * deviations recorded on it: nothing of that link is ever written in a document. A deviation is a
7271
+ * signed event of the journal: an agent (the QA reviewer) found the delivered work drifting from
7272
+ * requirements a started story cites. Its id, like an item of deferred work's, is `V-` then the
7273
+ * last 10 characters of the ULID of its event, never written in the `payload`, never reused.
6023
7274
  */
6024
- /** Event types of the devices of trust, each at version 1. */
6025
- const DEVICE_TRUST_EVENT_TYPES = {
6026
- paired: "device.paired",
6027
- revoked: "device.revoked",
6028
- rootCertified: "device.root_certified",
6029
- confirmed: "device.confirmed",
6030
- dismissed: "trust.dismissed"
7275
+ /** Event types of the deviations, each at version 1. */
7276
+ const DEVIATION_EVENT_TYPES = {
7277
+ reported: "deviation.reported",
7278
+ resolved: "deviation.resolved"
6031
7279
  };
6032
- /**
6033
- * The types the engine writes as a system actor (the bridge, or a creator's terminal for the
6034
- * first ones): refused in any write by an agent. `trust.dismissed` is the creator's (signed by
6035
- * the creator's key).
6036
- */
6037
- const DEVICE_TRUST_SYSTEM_EVENT_TYPES = /* @__PURE__ */ new Set([
6038
- DEVICE_TRUST_EVENT_TYPES.paired,
6039
- DEVICE_TRUST_EVENT_TYPES.revoked,
6040
- DEVICE_TRUST_EVENT_TYPES.rootCertified,
6041
- DEVICE_TRUST_EVENT_TYPES.confirmed
6042
- ]);
6043
- new Set(Object.values(DEVICE_TRUST_EVENT_TYPES));
6044
- /** The kinds of device a `device.paired` registers (the creator's key has its own events). */
6045
- const PAIRED_DEVICE_KINDS = [
6046
- "machine",
6047
- "phone",
6048
- "browser"
7280
+ /** Where a requirement stands: to do, in development, in review, delivered, or deviating. */
7281
+ const REQUIREMENT_STATUSES = [
7282
+ "todo",
7283
+ "in_development",
7284
+ "in_review",
7285
+ "delivered",
7286
+ "deviation"
6049
7287
  ];
6050
- /** `machine:`, `human:`, `phone:` or `browser:` then 16 base64url characters. */
6051
- const DEVICE_ID_PATTERN = /^(machine|human|phone|browser):[A-Za-z0-9_-]{16}$/;
6052
- /** The identifier of a device `device.paired` registers. */
6053
- const PAIRED_DEVICE_ID_PATTERN = /^(machine|phone|browser):[A-Za-z0-9_-]{16}$/;
6054
- /** The identifier of a bridge. */
6055
- const BRIDGE_ID_PATTERN = /^machine:[A-Za-z0-9_-]{16}$/;
6056
- /** The kind of identifier (`machine`, `human`, `phone`, `browser`), or undefined when malformed. */
6057
- function deviceKindOf(device) {
6058
- if (!DEVICE_ID_PATTERN.test(device)) return void 0;
6059
- return device.slice(0, device.indexOf(":"));
6060
- }
6061
- /** The algorithms of the linked devices (the `webauthn-*` ones serve the terminal's key only). */
6062
- const DEVICE_SIG_ALG = "ed25519";
6063
- const DEVICE_ENC_ALG = "x25519";
6064
- /** 32 bytes in base64url: 43 characters, the last one carrying 4 bits only. */
6065
- const PUBLIC_KEY_32 = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
6066
- /** 16 random bytes in base64url: 22 characters, the last one carrying 2 bits only. */
6067
- const RANDOM_16 = /^[A-Za-z0-9_-]{21}[AQgw]$/;
6068
- /** `sha256:` then the 32-byte digest in base64url (an event's hash). */
6069
- const TRUST_EVENT_HASH_PATTERN = /^sha256:[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
6070
- const publicKey32 = z.string().regex(PUBLIC_KEY_32).refine(isCanonicalBase64url);
6071
- const utcDatetime = z.iso.datetime();
6072
- /** A public key of a device with its algorithm: `{ alg, key }`. */
6073
- const DeviceSigKeySchema = z.strictObject({
6074
- alg: z.literal(DEVICE_SIG_ALG),
6075
- key: publicKey32
6076
- });
6077
- const DeviceEncKeySchema = z.strictObject({
6078
- alg: z.literal(DEVICE_ENC_ALG),
6079
- key: publicKey32
6080
- });
6081
- /**
6082
- * Every signed structure of trust carries its label `t` (type and version), and a key signs the
6083
- * types it knows only: an authorization is never taken for an order, nor a relay's challenge for
6084
- * an authorization. The domain separation stops a signature made for one purpose from
6085
- * serving another.
6086
- */
6087
- const TRUST_SIGNATURE_TAGS = {
6088
- devicePaired: "device.paired.auth/v1",
6089
- deviceRevoked: "device.revoked.auth/v1",
6090
- pairOffer: "pair.offer/v1",
6091
- rootCertified: "device.root_certified.auth/v1",
6092
- message: "msg.inner/v1",
6093
- linkWelcome: "link.welcome/v1"
6094
- };
6095
- /** True for a label this engine knows. */
6096
- function isTrustSignatureTag(tag) {
6097
- return Object.values(TRUST_SIGNATURE_TAGS).includes(tag);
7288
+ /** The requirements traced: the functional and non-functional ones of the PRD. */
7289
+ const TRACED_REQUIREMENT_PATTERN = /^(?:EF|ENF)-[1-9]\d*$/;
7290
+ /** A traced requirement as typed (`ef-18`, with spaces): trimmed, in upper case, then checked. */
7291
+ const TracedRequirementInputSchema = z.string().trim().toUpperCase().pipe(z.string().regex(TRACED_REQUIREMENT_PATTERN, { message: "expected EF-n or ENF-n, for example EF-18" }));
7292
+ /** `V-` then the last 10 characters of the ULID of the event that reported the deviation. */
7293
+ const DeviationIdSchema = z.string().regex(/^V-[0-9A-HJKMNP-TV-Z]{10}$/, { message: "expected V- then 10 characters, for example V-7M2K9QX4TB" });
7294
+ /** An id as typed (`v-7m2k9qx4tb`, with spaces): trimmed and in upper case, then checked. */
7295
+ const DeviationIdInputSchema = z.string().trim().toUpperCase().pipe(DeviationIdSchema);
7296
+ /** The id of the deviation reported by the event `eventId`: stable, never renumbered. */
7297
+ function deviationIdOf(eventId) {
7298
+ return `V-${eventId.slice(-10)}`;
6098
7299
  }
7300
+ const NO_CONTROL$2 = { message: "expected no control character" };
7301
+ const SummarySchema$1 = z.string().trim().min(1).max(200).refine((summary) => !/[\r\n]/.test(summary), { message: "expected a single line" }).refine((summary) => !hasControlCharacter(summary, false), NO_CONTROL$2);
7302
+ const DetailSchema = z.string().trim().min(1).max(2e3).refine((detail) => !hasControlCharacter(detail, true), NO_CONTROL$2);
7303
+ const ReasonSchema = z.string().trim().min(1).max(1e3).refine((reason) => !hasControlCharacter(reason, true), NO_CONTROL$2);
7304
+ const RequirementIdSchema = z.string().regex(REQUIREMENT_ID_PATTERN, { message: "expected a requirement id (EF-n, ENF-n, PU-n, MS-n or AD-n)" });
7305
+ const requirementsOf = (id) => z.array(id).min(1).max(20).refine((ids) => new Set(ids).size === ids.length, { message: "requirements must differ" });
6099
7306
  /**
6100
- * The order of a trusted device that offered a pairing (`pair.offer`): one rendezvous
6101
- * `x`, valid until `expires_at`, for one device of `kind`. The bridge writes `device.paired` on
6102
- * its authority, once.
6103
- */
6104
- const PairOfferSchema = z.strictObject({
6105
- x: z.string().regex(RANDOM_16).refine(isCanonicalBase64url),
6106
- expires_at: utcDatetime
6107
- });
6108
- /**
6109
- * The proof that the creator wanted this: the signature, by a device already trusted (or by the
6110
- * root, from the command run on the root machine), of the record of the event (`order` absent) or
6111
- * of the signed order `pair.offer` (`order` present).
7307
+ * `payload` of `deviation.reported`. The story is the envelope's (required); `pr` is the number of
7308
+ * the story's pull request when the deviation was reported, set by the engine from the journal.
6112
7309
  */
6113
- const AuthorizationSchema = z.strictObject({
6114
- signer: z.string().regex(DEVICE_ID_PATTERN),
6115
- alg: z.literal(DEVICE_SIG_ALG),
6116
- sig: Ed25519SignatureSchema,
6117
- order: PairOfferSchema.optional()
6118
- });
6119
- /** `payload` of `device.paired` v1. */
6120
- const DevicePairedPayloadSchema = z.strictObject({
6121
- device: z.string().regex(PAIRED_DEVICE_ID_PATTERN),
6122
- kind: z.enum(PAIRED_DEVICE_KINDS),
6123
- sig: DeviceSigKeySchema,
6124
- enc: DeviceEncKeySchema,
6125
- /** The bridge of the perimeter: the authorization holds in the journals this bridge writes. */
6126
- bridge: z.string().regex(BRIDGE_ID_PATTERN),
6127
- /** True until the handover is complete (`device.confirmed`): the bridge alone may revoke it. */
6128
- provisional: z.boolean(),
6129
- issued_at: utcDatetime,
6130
- authorization: AuthorizationSchema
6131
- }).refine((payload) => payload.device.startsWith(`${payload.kind}:`), {
6132
- path: ["kind"],
6133
- message: "the kind is the prefix of the device"
6134
- });
6135
- /** `payload` of `device.revoked` v1. */
6136
- const DeviceRevokedPayloadSchema = z.strictObject({
6137
- device: z.string().regex(PAIRED_DEVICE_ID_PATTERN),
6138
- /**
6139
- * For a machine that writes to the journal: the hash of its last event the author knew. The
6140
- * projection accepts of a revoked machine only the ancestors of this head (and of the heads of
6141
- * the other revocations of it). Null for a device that writes nothing.
6142
- */
6143
- chain_head: z.string().regex(TRUST_EVENT_HASH_PATTERN).nullable(),
6144
- issued_at: utcDatetime,
6145
- author: z.string().regex(DEVICE_ID_PATTERN),
6146
- authorization: z.strictObject({
6147
- alg: z.literal(DEVICE_SIG_ALG),
6148
- sig: Ed25519SignatureSchema
6149
- })
6150
- }).refine((payload) => payload.chain_head === null || payload.device.startsWith("machine:"), {
6151
- path: ["chain_head"],
6152
- message: "only a machine has a chain head"
6153
- });
6154
- /** `payload` of `device.root_certified` v1: the first phone recognises the root. */
6155
- const DeviceRootCertifiedPayloadSchema = z.strictObject({
6156
- root: z.strictObject({
6157
- device: z.string().regex(DEVICE_ID_PATTERN),
6158
- key: publicKey32
6159
- }),
6160
- issued_at: utcDatetime,
6161
- authorization: AuthorizationSchema
6162
- });
6163
- /** `payload` of `device.confirmed` v1: the handover of the device is complete. */
6164
- const DeviceConfirmedPayloadSchema = z.strictObject({
6165
- device: z.string().regex(PAIRED_DEVICE_ID_PATTERN),
6166
- issued_at: utcDatetime
6167
- });
6168
- /** `payload` of `trust.dismissed` v1: the events (their hashes) the creator dismisses. */
6169
- const TrustDismissedPayloadSchema = z.strictObject({
6170
- events: z.array(z.string().regex(TRUST_EVENT_HASH_PATTERN)).min(1).max(16),
6171
- issued_at: utcDatetime
7310
+ const DeviationReportedPayloadSchema = z.strictObject({
7311
+ requirements: requirementsOf(RequirementIdSchema),
7312
+ summary: SummarySchema$1,
7313
+ detail: DetailSchema.optional(),
7314
+ pr: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER).optional(),
7315
+ /** The commit compared with the requirements. */
7316
+ commit: CommitIdSchema.optional()
6172
7317
  });
6173
- /** The pinned root of a product, kept locally outside the repository (written by `pair`). */
6174
- const RootPinSchema = z.strictObject({
6175
- device: z.string().regex(/^(machine|human):[A-Za-z0-9_-]{16}$/),
6176
- public_key: publicKey32
7318
+ /** `payload` of `deviation.resolved`: the deviation, and how it was fixed or why it is lifted. */
7319
+ const DeviationResolvedPayloadSchema = z.strictObject({
7320
+ deviation: DeviationIdSchema,
7321
+ reason: ReasonSchema
7322
+ });
7323
+ /** A requirement id as typed: in upper case, then checked. */
7324
+ const RequirementInputSchema = z.string().trim().toUpperCase().pipe(RequirementIdSchema);
7325
+ /** Input of `report_deviation`: the story brought to its canonical form. */
7326
+ const ReportDeviationInputSchema = z.strictObject({
7327
+ agent: AgentIdSchema.max(64),
7328
+ story: StoryIdSchema.overwrite(canonicalStoryId),
7329
+ requirements: requirementsOf(RequirementInputSchema),
7330
+ summary: SummarySchema$1,
7331
+ detail: DetailSchema.optional(),
7332
+ commit: CommitIdSchema.optional()
7333
+ });
7334
+ /** Input of `lift_deviation`: the agent that reported it lifts it, with why. */
7335
+ const ResolveDeviationInputSchema = z.strictObject({
7336
+ agent: AgentIdSchema.max(64),
7337
+ deviation: DeviationIdInputSchema,
7338
+ reason: ReasonSchema
7339
+ });
7340
+ /** The reason the creator gives at the terminal (`trace resolve`). */
7341
+ const DeviationReasonSchema = ReasonSchema;
7342
+ /** Input of `get_trace`: one requirement, or every one when absent. */
7343
+ const GetTraceInputSchema = z.strictObject({ requirement: z.string().max(32).optional() });
7344
+ const DOCS_TEXT_MAX_CHARS = 2e3;
7345
+ /** Where a story stands, as the engine sees it. */
7346
+ const DOCS_STORY_STATUSES = [
7347
+ "todo",
7348
+ "in_progress",
7349
+ "in_review",
7350
+ "blocked",
7351
+ "done"
7352
+ ];
7353
+ const StoryRef = z.string().regex(/^[1-9]\d{0,5}-[1-9]\d{0,5}$/);
7354
+ const RequirementRef = z.string().regex(TRACED_REQUIREMENT_PATTERN).max(16);
7355
+ /** The request shapes (without the identifier, the version and the product, which the file reading adds). */
7356
+ const DocsListRequestFields = { op: z.literal("docs") };
7357
+ const DocsTraceRequestFields = {
7358
+ op: z.literal("trace"),
7359
+ requirement: RequirementRef
7360
+ };
7361
+ const DocsRetrosRequestFields = { op: z.literal("retros") };
7362
+ /** One document of the product: a file of `docs/` or `epics/` (the stories are told by the breakdown). */
7363
+ const DocsDocumentSchema = z.strictObject({
7364
+ /** The path from the product's folder, with `/`. */
7365
+ path: z.string().min(1).max(300),
7366
+ /** The header's kind (`prd`, `architecture`...), or `unknown` when the header is not valid. */
7367
+ kind: z.string().min(1).max(32),
7368
+ id: z.string().min(1).max(64).optional(),
7369
+ title: z.string().min(1).max(200).optional(),
7370
+ /** The agent that owns it, or `creator`. */
7371
+ owner: z.string().min(1).max(64).optional(),
7372
+ standing: z.string().min(1).max(32),
7373
+ /** The requirements it defines (active and retired), when it defines any. */
7374
+ requirements: z.int().min(0).max(1e5).optional(),
7375
+ /** The documents and requirements its header links to. */
7376
+ links: z.array(z.string().min(1).max(64)).max(40).optional()
7377
+ });
7378
+ const DocsRequirementSchema = z.strictObject({
7379
+ id: RequirementRef,
7380
+ text: z.string().max(400),
7381
+ /** The document that defines it. */
7382
+ path: z.string().min(1).max(300),
7383
+ /** Retired: the id stays reserved, the status below is not meaningful. */
7384
+ retired: z.boolean(),
7385
+ status: z.enum(REQUIREMENT_STATUSES),
7386
+ stories: z.array(StoryRef).max(100),
7387
+ /** The test files that cite it, or null when the tests could not be read. */
7388
+ tests: z.int().min(0).max(1e5).nullable(),
7389
+ pull_requests: z.int().min(0).max(1e5),
7390
+ /** Its open deviations. */
7391
+ deviations: z.int().min(0).max(1e5)
7392
+ });
7393
+ const DocsDeviationSchema = z.strictObject({
7394
+ id: z.string().regex(/^V-[0-9A-HJKMNP-TV-Z]{10}$/),
7395
+ story: StoryRef,
7396
+ requirements: z.array(z.string().min(1).max(16)).max(20),
7397
+ summary: z.string().max(200),
7398
+ reported_by: z.string().min(1).max(64),
7399
+ reported_at: z.string().max(40)
7400
+ });
7401
+ const DocsBreakdownStorySchema = z.strictObject({
7402
+ id: StoryRef,
7403
+ title: z.string().min(1).max(200).optional(),
7404
+ status: z.enum(DOCS_STORY_STATUSES),
7405
+ depends_on: z.array(StoryRef).max(40),
7406
+ requirements: z.array(RequirementRef).max(40)
7407
+ });
7408
+ const DocsEpicSchema = z.strictObject({
7409
+ /** `epic-2`. */
7410
+ id: z.string().regex(/^epic-[1-9]\d{0,5}$/),
7411
+ title: z.string().min(1).max(200).optional(),
7412
+ /** The path of the epic's document, when it has one. */
7413
+ path: z.string().min(1).max(300).optional(),
7414
+ out_of_scope: z.array(RequirementRef).max(200),
7415
+ stories: z.array(DocsBreakdownStorySchema).max(600)
7416
+ });
7417
+ const DocsCoverageSchema = z.strictObject({
7418
+ /** The active functional and non-functional requirements. */
7419
+ total: z.int().min(0).max(1e5),
7420
+ /** Those a story carries. */
7421
+ covered: z.int().min(0).max(1e5),
7422
+ /** Those an epic leaves aside. */
7423
+ out_of_scope: z.int().min(0).max(1e5),
7424
+ /** The others, by id. */
7425
+ uncovered: z.array(RequirementRef).max(200)
7426
+ });
7427
+ const DOCS_TESTS_STATES = [
7428
+ "read",
7429
+ "truncated",
7430
+ "unavailable"
7431
+ ];
7432
+ const CountsSchema = z.strictObject({
7433
+ todo: z.int().min(0),
7434
+ in_development: z.int().min(0),
7435
+ in_review: z.int().min(0),
7436
+ delivered: z.int().min(0),
7437
+ deviation: z.int().min(0)
7438
+ });
7439
+ const DocsListDataSchema = z.strictObject({
7440
+ op: z.literal("docs"),
7441
+ documents: z.array(DocsDocumentSchema).max(200),
7442
+ /** True when the product holds more documents than the bridge lists. */
7443
+ truncated: z.boolean(),
7444
+ requirements: z.array(DocsRequirementSchema).max(800),
7445
+ /** How many active requirements stand at each status. */
7446
+ counts: CountsSchema,
7447
+ /** How the tests were read: all of them, a part, or not at all. */
7448
+ tests: z.enum(DOCS_TESTS_STATES),
7449
+ /** The open deviations. */
7450
+ deviations: z.array(DocsDeviationSchema).max(50),
7451
+ epics: z.array(DocsEpicSchema).max(100),
7452
+ /** Absent while no story exists: nothing is covered or not yet. */
7453
+ coverage: DocsCoverageSchema.optional()
7454
+ });
7455
+ const DocsTraceDeviationSchema = DocsDeviationSchema.extend({
7456
+ detail: z.string().max(DOCS_TEXT_MAX_CHARS).optional(),
7457
+ status: z.enum(["open", "resolved"]),
7458
+ resolved: z.strictObject({
7459
+ reason: z.string().max(DOCS_TEXT_MAX_CHARS),
7460
+ by: z.string().min(1).max(64),
7461
+ at: z.string().max(40)
7462
+ }).optional()
7463
+ });
7464
+ const DocsTraceDataSchema = z.strictObject({
7465
+ op: z.literal("trace"),
7466
+ requirement: z.strictObject({
7467
+ id: RequirementRef,
7468
+ text: z.string().max(400),
7469
+ path: z.string().min(1).max(300),
7470
+ line: z.int().min(1).max(1e7),
7471
+ retired: z.boolean(),
7472
+ status: z.enum(REQUIREMENT_STATUSES),
7473
+ stories: z.array(z.strictObject({
7474
+ id: StoryRef,
7475
+ title: z.string().min(1).max(200).optional(),
7476
+ status: z.enum(DOCS_STORY_STATUSES)
7477
+ })).max(100),
7478
+ tests: z.array(z.strictObject({
7479
+ path: z.string().min(1).max(300),
7480
+ /** 0: the file's name cites it. */
7481
+ line: z.int().min(0).max(1e7)
7482
+ })).max(100),
7483
+ pull_requests: z.array(z.strictObject({
7484
+ story: StoryRef,
7485
+ number: z.int().min(1).max(1e9)
7486
+ })).max(100),
7487
+ deviations: z.array(DocsTraceDeviationSchema).max(100)
7488
+ }),
7489
+ tests_state: z.enum(DOCS_TESTS_STATES)
7490
+ });
7491
+ /** Where a retrospective stands: the creator's answer to its checkpoint decides. */
7492
+ const RETRO_STATUSES = [
7493
+ "open",
7494
+ "approved",
7495
+ "changes_requested",
7496
+ "dismissed"
7497
+ ];
7498
+ /**
7499
+ * What became of a proposed lesson: still `proposed` (nothing recorded), `active` (recorded and in
7500
+ * force), `retired` or `superseded` since, or `not_kept` (its retrospective was not approved).
7501
+ */
7502
+ const RETRO_LESSON_STATUSES = [
7503
+ "proposed",
7504
+ "active",
7505
+ "retired",
7506
+ "superseded",
7507
+ "not_kept"
7508
+ ];
7509
+ const LessonText = z.string().max(DOCS_TEXT_MAX_CHARS);
7510
+ const LessonRef = z.string().regex(/^L-[0-9A-HJKMNP-TV-Z]{10}-[1-9][0-9]?$/);
7511
+ const DocsRetroSchema = z.strictObject({
7512
+ id: z.string().regex(/^R-[0-9A-HJKMNP-TV-Z]{10}$/),
7513
+ scope: z.strictObject({
7514
+ kind: z.enum(["epic", "sprint"]),
7515
+ id: z.string().min(1).max(16)
7516
+ }),
7517
+ at: z.string().max(40),
7518
+ proposed_by: z.string().min(1).max(64),
7519
+ status: z.enum(RETRO_STATUSES),
7520
+ summary: LessonText,
7521
+ lessons: z.array(z.strictObject({
7522
+ id: LessonRef,
7523
+ title: z.string().max(200),
7524
+ text: LessonText,
7525
+ tags: z.array(z.string().min(1).max(32)).max(5),
7526
+ agents: z.array(z.string().min(1).max(64)).max(10),
7527
+ status: z.enum(RETRO_LESSON_STATUSES)
7528
+ })).max(12),
7529
+ retire: z.array(z.strictObject({
7530
+ lesson: LessonRef,
7531
+ reason: LessonText
7532
+ })).max(12),
7533
+ actions: z.array(z.strictObject({
7534
+ id: z.string().min(1).max(32),
7535
+ text: LessonText,
7536
+ owner: z.string().min(1).max(64).optional()
7537
+ })).max(12)
7538
+ });
7539
+ /** A lesson in force: what the agents read at the start of a task. */
7540
+ const DocsMemoryLessonSchema = z.strictObject({
7541
+ id: LessonRef,
7542
+ title: z.string().max(200),
7543
+ text: LessonText,
7544
+ tags: z.array(z.string().min(1).max(32)).max(5),
7545
+ agents: z.array(z.string().min(1).max(64)).max(10),
7546
+ at: z.string().max(40),
7547
+ /** The retrospective it comes from. */
7548
+ retro: z.string().regex(/^R-[0-9A-HJKMNP-TV-Z]{10}$/),
7549
+ /** True when it was also promoted to the common memory of the machine. */
7550
+ promoted: z.boolean()
7551
+ });
7552
+ const DocsRetrosDataSchema = z.strictObject({
7553
+ op: z.literal("retros"),
7554
+ retrospectives: z.array(DocsRetroSchema).max(40),
7555
+ /** The lessons in force, newest first. */
7556
+ memory: z.array(DocsMemoryLessonSchema).max(120),
7557
+ /**
7558
+ * The retrospectives whose content this machine could not read (the product's key is missing
7559
+ * here): they are counted, never guessed.
7560
+ */
7561
+ unreadable: z.int().min(0).max(1e5)
6177
7562
  });
6178
- /**
6179
- * What the authorization of a `device.paired` signs: its payload without `authorization`, plus the
6180
- * label. Independent of the product and of `prev`: the bridge writes it in each journal it serves,
6181
- * but cannot make one without the signer.
6182
- */
6183
- function devicePairedRecord(payload) {
6184
- return {
6185
- t: TRUST_SIGNATURE_TAGS.devicePaired,
6186
- device: payload.device,
6187
- kind: payload.kind,
6188
- sig: payload.sig,
6189
- enc: payload.enc,
6190
- bridge: payload.bridge,
6191
- provisional: payload.provisional,
6192
- issued_at: payload.issued_at
6193
- };
6194
- }
6195
- /** What the signed order `pair.offer` covers. */
6196
- function pairOfferRecord(input) {
6197
- return {
6198
- t: TRUST_SIGNATURE_TAGS.pairOffer,
6199
- kind: input.kind,
6200
- bridge: input.bridge,
6201
- x: input.order.x,
6202
- expires_at: input.order.expires_at
6203
- };
6204
- }
6205
- /** What the authorization of a `device.revoked` signs. */
6206
- function deviceRevokedRecord(payload) {
6207
- return {
6208
- t: TRUST_SIGNATURE_TAGS.deviceRevoked,
6209
- device: payload.device,
6210
- chain_head: payload.chain_head,
6211
- issued_at: payload.issued_at,
6212
- author: payload.author
6213
- };
6214
- }
6215
- /** What the authorization of a `device.root_certified` signs. */
6216
- function rootCertifiedRecord(payload) {
6217
- return {
6218
- t: TRUST_SIGNATURE_TAGS.rootCertified,
6219
- root: payload.root,
6220
- issued_at: payload.issued_at
6221
- };
6222
- }
6223
7563
  //#endregion
6224
7564
  //#region ../../packages/protocol/dist/elicitation.js
6225
7565
  /**
@@ -6322,6 +7662,25 @@ const CheckpointElicitedPayloadSchema = z.strictObject({
6322
7662
  });
6323
7663
  });
6324
7664
  //#endregion
7665
+ //#region ../../packages/protocol/dist/exchange.js
7666
+ /**
7667
+ * The summary of an exchange: at the end of each exchange with the creator, Noah writes a short
7668
+ * note to the journal (what was asked, what was decided, what was promised). Whatever session Noah
7669
+ * runs in (the AI tool's or the bridge's), a fresh session reads the latest notes in the state of
7670
+ * the product and takes the thread up where it was left, without any session to resume.
7671
+ */
7672
+ /** Event type of the summary, at version 1. */
7673
+ const EXCHANGE_EVENT_TYPES = { summarized: "exchange.summarized" };
7674
+ /** One line, 1 to 300 characters, no control character. */
7675
+ const SummarySchema = z.string().trim().min(1).max(300).refine((text) => !hasControlCharacter(text, false), { message: "expected one line without control character" }).refine((text) => !/[\u2028\u2029\u200e\u200f\u202a-\u202e\u2066-\u2069]/u.test(text), { message: "expected one line without separator or direction mark" });
7676
+ /** Input of `summarize`: Noah's own id and the note. */
7677
+ const SummarizeInputSchema = z.strictObject({
7678
+ agent: AgentIdSchema.max(64),
7679
+ summary: SummarySchema
7680
+ });
7681
+ /** `payload` of `exchange.summarized`. */
7682
+ const ExchangeSummarizedPayloadSchema = z.strictObject({ summary: SummarySchema });
7683
+ //#endregion
6325
7684
  //#region ../../packages/protocol/dist/merge.js
6326
7685
  /**
6327
7686
  * The merge of a story. Once the creator approves a story's merge checkpoint
@@ -6346,14 +7705,18 @@ const DEFAULT_MERGE_METHOD = "merge";
6346
7705
  * 3.28: `forge`, a merge the creator did on the forge itself (the pull request merged by hand),
6347
7706
  * which the engine recorded from what the forge shows: no approval to wait for, the forge's own
6348
7707
  * event is the proof (who merged it, when, on which commit). `agent`, the merge the
6349
- * agent responsible decided because the creator did not keep the merges for themselves.
7708
+ * agent responsible decided because the creator did not keep the merges for themselves. `web`, the
7709
+ * merge the machine did once the signed order of a browser the creator trusts was carried out, in
7710
+ * the same write as that answer; `mobile`, the same for the signed order of a phone.
6350
7711
  */
6351
7712
  const MERGE_SOURCES = [
6352
7713
  "chat",
6353
7714
  "elicitation",
6354
7715
  "terminal",
6355
7716
  "forge",
6356
- "agent"
7717
+ "agent",
7718
+ "web",
7719
+ "mobile"
6357
7720
  ];
6358
7721
  /** Most changed files one merge records (the knowledge base reads them). */
6359
7722
  const MAX_MERGED_FILES = 1e3;
@@ -6767,6 +8130,281 @@ const LoopInputSchema = z.strictObject({
6767
8130
  path: ["story"]
6768
8131
  });
6769
8132
  //#endregion
8133
+ //#region ../../packages/protocol/dist/tolerant-reader.js
8134
+ const cache = /* @__PURE__ */ new WeakMap();
8135
+ const defOf = (schema) => schema._zod.def;
8136
+ const cloneWith = (schema, def) => schema.clone(def);
8137
+ function derive(schema) {
8138
+ const def = defOf(schema);
8139
+ switch (def.type) {
8140
+ case "object": {
8141
+ const shape = def.shape;
8142
+ const next = {};
8143
+ for (const key of Object.keys(shape)) next[key] = readTolerant(shape[key]);
8144
+ return cloneWith(schema, {
8145
+ ...def,
8146
+ shape: next,
8147
+ catchall: void 0
8148
+ });
8149
+ }
8150
+ case "array": return cloneWith(schema, {
8151
+ ...def,
8152
+ element: readTolerant(def.element)
8153
+ });
8154
+ case "optional":
8155
+ case "nullable":
8156
+ case "readonly":
8157
+ case "default":
8158
+ case "prefault":
8159
+ case "nonoptional":
8160
+ case "catch": return cloneWith(schema, {
8161
+ ...def,
8162
+ innerType: readTolerant(def.innerType)
8163
+ });
8164
+ case "union": return cloneWith(schema, {
8165
+ ...def,
8166
+ options: def.options.map((option) => readTolerant(option))
8167
+ });
8168
+ case "record": return cloneWith(schema, {
8169
+ ...def,
8170
+ valueType: readTolerant(def.valueType)
8171
+ });
8172
+ case "tuple": return cloneWith(schema, {
8173
+ ...def,
8174
+ items: def.items.map((item) => readTolerant(item))
8175
+ });
8176
+ case "pipe": return cloneWith(schema, {
8177
+ ...def,
8178
+ in: readTolerant(def.in),
8179
+ out: readTolerant(def.out)
8180
+ });
8181
+ default: return schema;
8182
+ }
8183
+ }
8184
+ /** The same schema, reading what a device receives: unknown keys dropped, known ones as strict as before. */
8185
+ function readTolerant(schema) {
8186
+ const known = cache.get(schema);
8187
+ if (known !== void 0) return known;
8188
+ const derived = derive(schema);
8189
+ cache.set(schema, derived);
8190
+ return derived;
8191
+ }
8192
+ const ProductStageSchema = z.enum([
8193
+ "framing",
8194
+ "development",
8195
+ "launch",
8196
+ "online"
8197
+ ]);
8198
+ /** How a product starts: from an idea (no repository), from a repository already on the machine, or from an empty repository. */
8199
+ const PRODUCT_STARTS = [
8200
+ "idea",
8201
+ "existing",
8202
+ "empty"
8203
+ ];
8204
+ /** What a machine says of the repository of a product: none, one it made empty, or one that was already there. */
8205
+ const PRODUCT_REPOSITORIES = [
8206
+ "none",
8207
+ "empty",
8208
+ "existing"
8209
+ ];
8210
+ const PRODUCT_IDEA_MAX = 4e3;
8211
+ /** The longest folder path a machine tells of a product (the full path on the machine). */
8212
+ const PRODUCT_FOLDER_PATH_MAX = 1e3;
8213
+ /** The identifier of a place a machine allows products in (`home`, `place-2`): the machine names it, the device only repeats it. */
8214
+ const PRODUCT_LOCATION_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
8215
+ const hasControl = (value) => [...value].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127);
8216
+ /** A name the creator gives a product: trimmed, one line, no control character. */
8217
+ function isProductName(value) {
8218
+ return value.length >= 1 && value.length <= 80 && value === value.trim() && !hasControl(value);
8219
+ }
8220
+ /**
8221
+ * A folder name inside a place the machine allows: one segment, never a path. It holds no
8222
+ * separator, no drive or stream mark, no control character, is not `.` or `..`, and does not end
8223
+ * with a dot or a space (which a system may drop silently).
8224
+ */
8225
+ function isFolderName(value) {
8226
+ return value.length >= 1 && value.length <= 100 && value !== "." && value !== ".." && !/[\\/:*?"<>|]/.test(value) && !hasControl(value) && !/[. ]$/.test(value);
8227
+ }
8228
+ /**
8229
+ * A step of the way into a place: one folder name, never a path, and never a hidden one (a name
8230
+ * that starts with a dot). The machine resolves the steps itself and refuses what leaves the place.
8231
+ */
8232
+ function isBrowseStep(value) {
8233
+ return isFolderName(value) && !value.startsWith(".");
8234
+ }
8235
+ z.strictObject({
8236
+ name: z.string().refine(isProductName, { message: "a name of one line, trimmed" }),
8237
+ start: z.enum(PRODUCT_STARTS),
8238
+ location: z.string().regex(PRODUCT_LOCATION_ID_PATTERN),
8239
+ folder: z.string().refine(isFolderName, { message: "a folder name, not a path" }).optional(),
8240
+ /** The folders to go through inside the place, one name each (as the machine listed them), to make the product there. */
8241
+ path: z.array(z.string().refine(isBrowseStep, { message: "a folder name, not a path" })).max(12).optional(),
8242
+ preset: z.enum(PROFILE_PRESET_IDS),
8243
+ stage: ProductStageSchema,
8244
+ idea: z.string().min(1).max(PRODUCT_IDEA_MAX).optional()
8245
+ }).superRefine((params, context) => {
8246
+ if (params.start === "existing" && params.folder === void 0) context.addIssue({
8247
+ code: "custom",
8248
+ path: ["folder"],
8249
+ message: "a repository to take"
8250
+ });
8251
+ if (params.start === "existing" && params.path !== void 0) context.addIssue({
8252
+ code: "custom",
8253
+ path: ["path"],
8254
+ message: "not for an existing one"
8255
+ });
8256
+ if (params.start !== "existing" && params.folder !== void 0) context.addIssue({
8257
+ code: "custom",
8258
+ path: ["folder"],
8259
+ message: "only for an existing one"
8260
+ });
8261
+ if (params.start !== "idea" && params.idea !== void 0) context.addIssue({
8262
+ code: "custom",
8263
+ path: ["idea"],
8264
+ message: "only with an idea"
8265
+ });
8266
+ });
8267
+ z.strictObject({});
8268
+ z.strictObject({
8269
+ stage: ProductStageSchema.optional(),
8270
+ paused: z.boolean().optional(),
8271
+ archived: z.boolean().optional()
8272
+ }).refine((params) => Object.keys(params).length > 0, { message: "at least one mark" });
8273
+ z.strictObject({ profile: ProfileSchema });
8274
+ z.strictObject({ to: SemverSchema.optional() });
8275
+ readTolerant(z.strictObject({
8276
+ version: SemverSchema,
8277
+ /** The version this machine would bring the product to; absent when it cannot update products. */
8278
+ target: SemverSchema.optional(),
8279
+ /** `running` while an update asked from a device is under way; `failed` when the last one did not end. */
8280
+ update: z.enum(["running", "failed"]).optional()
8281
+ }));
8282
+ /** What a machine says of the place it allows products in. */
8283
+ const ProductLocationSchema = z.strictObject({
8284
+ id: z.string().regex(PRODUCT_LOCATION_ID_PATTERN),
8285
+ /** The folder as the creator reads it (the home folder written `~`). */
8286
+ label: z.string().min(1).max(200),
8287
+ /** The folders of this place that hold a repository and no product yet: ones to start from. */
8288
+ folders: z.array(z.string().refine(isFolderName)).max(50),
8289
+ /** True for the place the creator chose the last time (the one to propose first). */
8290
+ last: z.literal(true).optional()
8291
+ });
8292
+ /**
8293
+ * A creation the machine is making, has made or could not make. It is told by its ticket (the
8294
+ * reference the order answered with); a finished one names the product it made. A finished or
8295
+ * failed one stays a few minutes in the report, so that a device that was away still learns it.
8296
+ */
8297
+ const ProductCreationSchema = z.strictObject({
8298
+ id: UlidSchema,
8299
+ name: z.string().min(1).max(80),
8300
+ state: z.enum([
8301
+ "running",
8302
+ "done",
8303
+ "failed"
8304
+ ]),
8305
+ /** The product made, once `done`. */
8306
+ product: UlidSchema.optional(),
8307
+ /** Why it failed. */
8308
+ code: z.enum([
8309
+ "folder",
8310
+ "repository",
8311
+ "engine",
8312
+ "link"
8313
+ ]).optional()
8314
+ });
8315
+ /** The marks and the profile a machine reports for a product it serves. */
8316
+ const ProductLifeSchema = z.strictObject({
8317
+ stage: ProductStageSchema.optional(),
8318
+ paused: z.literal(true).optional(),
8319
+ archived: z.literal(true).optional(),
8320
+ repository: z.enum(PRODUCT_REPOSITORIES).optional(),
8321
+ /** The full path of the product's folder on this machine: told to the creator's own devices only, never published. */
8322
+ folder: z.string().min(1).max(PRODUCT_FOLDER_PATH_MAX).optional(),
8323
+ profile: ProfileSchema.optional(),
8324
+ autonomy: ProductAutonomySchema.optional()
8325
+ });
8326
+ //#endregion
8327
+ //#region ../../packages/protocol/dist/machines.js
8328
+ /**
8329
+ * Several machines for one account: what a machine says about itself (end-to-end encrypted, the
8330
+ * relay never reads it), the choice of a product's main bridge, and the rules that route an order.
8331
+ *
8332
+ * Generic on purpose: schemas and small pure rules only, no transport and no account.
8333
+ *
8334
+ * Reading rule: a device ignores the optional fields of a report it does not know and validates
8335
+ * those it knows (`MachineReportReader`); an order a machine receives stays strict.
8336
+ */
8337
+ /** Event types of the machines, each at version 1. */
8338
+ const MACHINE_EVENT_TYPES = {
8339
+ /** The main bridge of the product changed: written by the machine that becomes main. */
8340
+ primaryChanged: "bridge.primary_changed" };
8341
+ /**
8342
+ * `payload` of `bridge.primary_changed`: the bridge that is now the product's main one, and the
8343
+ * one that was (null for the first choice). Only the last one in projection order counts.
8344
+ */
8345
+ const PrimaryChangedPayloadSchema = z.strictObject({
8346
+ bridge: z.string().regex(BRIDGE_ID_PATTERN),
8347
+ previous: z.string().regex(BRIDGE_ID_PATTERN).nullable()
8348
+ });
8349
+ /** One AI tool the bridge detected on its machine, and how it is integrated. */
8350
+ const DetectedToolSchema = z.strictObject({
8351
+ tool: z.string().min(1).max(40),
8352
+ /** The integration mode, in plain words (never a path). */
8353
+ mode: z.string().min(1).max(60),
8354
+ active: z.boolean()
8355
+ });
8356
+ /**
8357
+ * What a bridge says of its machine, inside its end-to-end encrypted beat: its version, the
8358
+ * protocol version it speaks, the products it serves (names the creator knows), how many agent
8359
+ * instances run (an instance is an agent on a story), the tools it detected and the role the
8360
+ * creator gave it. The relay sees none of it.
8361
+ */
8362
+ const MachineReportSchema = z.strictObject({
8363
+ /** The name the machine goes by (its host name unless the creator changed it). */
8364
+ name: z.string().min(1).max(60),
8365
+ bridge_version: SemverSchema,
8366
+ protocol_version: z.number().int().positive(),
8367
+ products: z.array(z.strictObject({
8368
+ id: z.string().min(1).max(80),
8369
+ name: z.string().min(1).max(80),
8370
+ /** The creator's priority for this product (1 first); absent while they gave none. */
8371
+ rank: z.int().min(1).max(9999).optional(),
8372
+ /** The marks the creator gave it (stage, pause, archive), its repository and its profile. */
8373
+ ...ProductLifeSchema.shape
8374
+ })).max(64),
8375
+ agents: z.number().int().nonnegative(),
8376
+ tools: z.array(DetectedToolSchema).max(64),
8377
+ role: z.string().max(120).optional(),
8378
+ /**
8379
+ * The names of the tokens and logins the creator gave this machine (`forge.github`,
8380
+ * `data.crm`), never their values: what to renew at the platforms if the machine is lost. Only
8381
+ * present when there is at least one.
8382
+ */
8383
+ secrets: z.array(z.string().regex(/^(?:forge|data)\.[a-z0-9][a-z0-9._-]{0,63}$/)).max(64).optional(),
8384
+ /**
8385
+ * The code platforms this machine is linked to (or being linked to): who, how far, until when.
8386
+ * Never a token. Only present when there is at least one.
8387
+ */
8388
+ forges: z.array(ForgeLinkStatusSchema).max(16).optional(),
8389
+ /**
8390
+ * Where this machine keeps its keys: the system keychain, or the fallback store of a machine
8391
+ * that has none (a server). Absent from a bridge that does not say.
8392
+ */
8393
+ key_store: z.enum(["system", "fallback"]).optional(),
8394
+ /**
8395
+ * The places this machine allows new products in, with the repositories already there that a
8396
+ * product can start from. Only present on a machine that can create products.
8397
+ */
8398
+ locations: z.array(ProductLocationSchema).max(8).optional(),
8399
+ /** The products this machine is creating or failed to create. Only present when there is one. */
8400
+ creations: z.array(ProductCreationSchema).max(8).optional()
8401
+ });
8402
+ readTolerant(MachineReportSchema);
8403
+ z.strictObject({
8404
+ bridge: z.string().regex(BRIDGE_ID_PATTERN),
8405
+ role: z.string().max(120)
8406
+ });
8407
+ //#endregion
6770
8408
  //#region ../../packages/protocol/dist/merge-order.js
6771
8409
  /**
6772
8410
  * The order of the merges and the conflicts with the main branch. The engine
@@ -6926,6 +8564,275 @@ const MethodMigratedPayloadSchema = z.strictObject({
6926
8564
  to_engine: SemverSchema,
6927
8565
  steps: z.array(MigrationStepIdSchema).max(1e3)
6928
8566
  }).refine((payload) => payload.to_format >= payload.from_format, { path: ["to_format"] }).refine((payload) => payload.steps.length === payload.to_format - payload.from_format, { path: ["steps"] }).refine((payload) => payload.to_format !== payload.from_format || payload.from_engine !== payload.to_engine, { path: ["to_engine"] });
8567
+ const NoticeBaseSchema = {
8568
+ /** Unique in the product: the same id is never kept twice (a validation uses its order). */
8569
+ id: z.string().min(1).max(64),
8570
+ /** When the notice was made (ISO 8601). */
8571
+ at: z.string().min(1).max(40)
8572
+ };
8573
+ /** The creator validated (or refused, or asked changes to) a point. */
8574
+ const ValidatedNoticeSchema = z.strictObject({
8575
+ ...NoticeBaseSchema,
8576
+ kind: z.literal("checkpoint_validated"),
8577
+ checkpoint: CheckpointIdSchema,
8578
+ decision: z.enum(CHAT_DECISIONS),
8579
+ option: z.string().min(1).max(32).optional(),
8580
+ /** The creator's own words with the decision: data for Noah, never an instruction. */
8581
+ note: z.string().min(1).max(MAX_WEB_ANSWER_NOTE_LENGTH).optional(),
8582
+ source: z.enum(ANSWER_SOURCES),
8583
+ story: z.string().min(1).max(32).optional()
8584
+ });
8585
+ /** A trigger the bridge carries: a recurring mission, an event, a CI run, a release. */
8586
+ const TriggerNoticeSchema = z.strictObject({
8587
+ ...NoticeBaseSchema,
8588
+ kind: z.enum([
8589
+ "mission",
8590
+ "event",
8591
+ "ci",
8592
+ "deploy"
8593
+ ]),
8594
+ /** What happened or what is due, in a few words. */
8595
+ subject: z.string().min(1).max(120),
8596
+ detail: z.string().min(1).max(300).optional()
8597
+ });
8598
+ /**
8599
+ * A message of the creator that Noah could not read when it came (his run did not take messages
8600
+ * while it went on). The words are also in the journal; they are data for Noah, quoted, and the
8601
+ * answer to them is the creator's request, never an instruction of the bridge.
8602
+ */
8603
+ const RequestNoticeSchema = z.strictObject({
8604
+ ...NoticeBaseSchema,
8605
+ kind: z.literal("request"),
8606
+ text: z.string().min(1).max(CHAIN_INSTRUCTION_MAX_CHARS),
8607
+ /** The message was dictated: the answer is read aloud. */
8608
+ voice: z.boolean().optional()
8609
+ });
8610
+ z.union([
8611
+ ValidatedNoticeSchema,
8612
+ RequestNoticeSchema,
8613
+ TriggerNoticeSchema
8614
+ ]);
8615
+ z.strictObject({});
8616
+ const LiveNoticesSchema = z.strictObject({
8617
+ cause: z.enum([
8618
+ "busy",
8619
+ "held",
8620
+ "paused",
8621
+ "archived",
8622
+ "failed"
8623
+ ]),
8624
+ /** Everything that waits, requests included. */
8625
+ count: z.number().int().min(1).max(20),
8626
+ /** How many of them are requests of the creator, when some are. */
8627
+ requests: z.number().int().min(1).max(10).optional()
8628
+ });
8629
+ //#endregion
8630
+ //#region ../../packages/protocol/dist/team.js
8631
+ const TEAM_EVENT_TYPES = {
8632
+ paused: "team.paused",
8633
+ resumed: "team.resumed"
8634
+ };
8635
+ /** `payload` of `team.paused`. */
8636
+ const TeamPausedPayloadSchema = z.strictObject({ reason: z.enum(["creator", "emergency_stop"]) });
8637
+ /** `payload` of `team.resumed`. */
8638
+ const TeamResumedPayloadSchema = z.strictObject({});
8639
+ z.strictObject({});
8640
+ /**
8641
+ * Input of `pause_team`: Noah's own id, nothing else. Noah writes the pause only once the creator
8642
+ * said yes in the conversation (he proposes it, he never imposes it); the resumption stays the
8643
+ * creator's gesture.
8644
+ */
8645
+ const PauseTeamInputSchema = z.strictObject({ agent: AgentIdSchema.max(64) });
8646
+ //#endregion
8647
+ //#region ../../packages/protocol/dist/orders.js
8648
+ /**
8649
+ * Signed orders: every command sent to a machine is signed by the device of the creator that gives
8650
+ * it, and the bridge that receives it checks it with the key its journal registers for that
8651
+ * device, never with a key the relay supplied. An order is one JSON object; the signature covers
8652
+ * its canonical form (RFC 8785) without the signature itself, `t` included. `t` names the order
8653
+ * type and its version (`order.pair.offer/v1`), so that a signature made for an order is never
8654
+ * taken for an authorization, a message or an event, and conversely.
8655
+ *
8656
+ * Adding an order type means declaring its parameters, its target and its risk level in an
8657
+ * `OrderTypeRegistry`; no check below changes.
8658
+ */
8659
+ /** Event type the bridge writes once an order is applied: it keeps the signed order. */
8660
+ const ORDER_EVENT_TYPES = {
8661
+ applied: "order.applied",
8662
+ /** A signed order the bridge refused: the journal keeps who gave it and why it was refused. */
8663
+ rejected: "order.rejected"
8664
+ };
8665
+ /** Orders are signed with Ed25519 only. */
8666
+ const ORDER_ALG = DEVICE_SIG_ALG;
8667
+ /** The label of the receipt a bridge signs for an order. */
8668
+ const ORDER_RECEIPT_TAG = "order.receipt/v1";
8669
+ const ORDER_TAG = /^order\.([a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*)\/v([1-9][0-9]{0,2})$/;
8670
+ /** The codes a bridge answers an order with, in the order the bridge applies its checks. */
8671
+ const ORDER_ERROR_CODES = {
8672
+ INVALID_ORDER: "INVALID_ORDER",
8673
+ UPGRADE_REQUIRED: ErrorCode.UPGRADE_REQUIRED,
8674
+ UNKNOWN_DEVICE: "UNKNOWN_DEVICE",
8675
+ SIGNATURE_INVALID: ErrorCode.SIGNATURE_INVALID,
8676
+ SECURITY_ROOT_CHANGED: ErrorCode.SECURITY_ROOT_CHANGED,
8677
+ DEVICE_REVOKED: "DEVICE_REVOKED",
8678
+ WRONG_BRIDGE: "WRONG_BRIDGE",
8679
+ ORDER_EXPIRED: "ORDER_EXPIRED",
8680
+ ORDER_REPLAYED: "ORDER_REPLAYED",
8681
+ NOT_OWNER: "NOT_OWNER",
8682
+ STALE_BASIS: "STALE_BASIS",
8683
+ /** Two orders on the same target: the first one applied wins, the second gets this. */
8684
+ ALREADY_RESOLVED: "ALREADY_RESOLVED"
8685
+ };
8686
+ ORDER_ERROR_CODES.INVALID_ORDER, ORDER_ERROR_CODES.UPGRADE_REQUIRED, ORDER_ERROR_CODES.UNKNOWN_DEVICE, ORDER_ERROR_CODES.SIGNATURE_INVALID, ORDER_ERROR_CODES.SECURITY_ROOT_CHANGED, ORDER_ERROR_CODES.DEVICE_REVOKED, ORDER_ERROR_CODES.WRONG_BRIDGE, ORDER_ERROR_CODES.ORDER_EXPIRED, ORDER_ERROR_CODES.ORDER_REPLAYED, ORDER_ERROR_CODES.NOT_OWNER, ORDER_ERROR_CODES.STALE_BASIS, ORDER_ERROR_CODES.ALREADY_RESOLVED;
8687
+ /** `sha256:` then the digest in base64url: the fingerprint of what the creator saw. */
8688
+ const ORDER_BASIS_PATTERN = TRUST_EVENT_HASH_PATTERN;
8689
+ z.strictObject({
8690
+ path: z.string().min(1).max(1024),
8691
+ branch: z.string().min(1).max(255),
8692
+ blob_sha: z.string().regex(/^[0-9a-f]{40}(?:[0-9a-f]{24})?$/)
8693
+ });
8694
+ const utcDatetime = z.iso.datetime();
8695
+ /**
8696
+ * An order, as it travels. `params` is checked again by the schema of its type (a registry
8697
+ * declares it); here it is only a JSON object.
8698
+ */
8699
+ const OrderSchema = z.strictObject({
8700
+ /** `order.<type>/v<version>`. */
8701
+ t: z.string().regex(ORDER_TAG),
8702
+ id: UlidSchema,
8703
+ /** The device of the creator that signs the order. */
8704
+ device: z.string().regex(DEVICE_ID_PATTERN),
8705
+ /** The bridge the order is for. */
8706
+ bridge: z.string().regex(BRIDGE_ID_PATTERN),
8707
+ product: UlidSchema,
8708
+ /** The story the order is about, or null. */
8709
+ target: z.string().min(1).max(120).nullable(),
8710
+ /** The fingerprint of the state and documents the creator saw, or null. */
8711
+ basis: z.string().regex(ORDER_BASIS_PATTERN).nullable(),
8712
+ /** The device's own counter: strictly increasing, one per order the device signs. */
8713
+ seq: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER),
8714
+ issued_at: utcDatetime,
8715
+ expires_at: utcDatetime,
8716
+ params: z.record(z.string(), z.unknown()),
8717
+ alg: z.literal(ORDER_ALG),
8718
+ sig: Ed25519SignatureSchema
8719
+ }).refine((order) => Date.parse(order.expires_at) > Date.parse(order.issued_at), {
8720
+ path: ["expires_at"],
8721
+ message: "an order expires after it is issued"
8722
+ }).refine((order) => jsonValueIssue(order.params) === void 0, {
8723
+ path: ["params"],
8724
+ message: "params are JSON"
8725
+ });
8726
+ z.strictObject({
8727
+ t: z.literal(ORDER_RECEIPT_TAG),
8728
+ order: UlidSchema,
8729
+ /** The device that gave the order: the receipt is for it only. */
8730
+ device: z.string().regex(DEVICE_ID_PATTERN),
8731
+ bridge: z.string().regex(BRIDGE_ID_PATTERN),
8732
+ product: UlidSchema,
8733
+ outcome: z.enum(["applied", "rejected"]),
8734
+ /** The code the emitting device sees (never `UNKNOWN_DEVICE`); present when rejected. */
8735
+ code: z.string().regex(/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$/).optional(),
8736
+ /**
8737
+ * A simple handle on what an applied order made (never its content): for a secret, the
8738
+ * reference under which the machine keeps it.
8739
+ */
8740
+ reference: z.string().min(1).max(128).optional(),
8741
+ at: utcDatetime,
8742
+ alg: z.literal(ORDER_ALG),
8743
+ sig: Ed25519SignatureSchema
8744
+ }).refine((receipt) => receipt.outcome === "rejected" === (receipt.code !== void 0), {
8745
+ path: ["code"],
8746
+ message: "a rejected receipt names its code, an applied one has none"
8747
+ }).refine((receipt) => receipt.reference === void 0 || receipt.outcome === "applied", {
8748
+ path: ["reference"],
8749
+ message: "only an applied receipt carries a reference"
8750
+ }).refine((receipt) => receipt.code !== ORDER_ERROR_CODES.UNKNOWN_DEVICE, {
8751
+ path: ["code"],
8752
+ message: "the external code never tells that a device is unknown"
8753
+ });
8754
+ /** Where an order was given from, as the bridge knows it from the device's kind in its registry. */
8755
+ const ORDER_SOURCES = [
8756
+ "web",
8757
+ "mobile",
8758
+ "machine"
8759
+ ];
8760
+ /** `payload` of `order.applied` v1: the signed order, kept so that anyone can check it without the bridge. */
8761
+ const OrderAppliedPayloadSchema = z.strictObject({
8762
+ order: OrderSchema,
8763
+ /** The emitting device (the order's own `device`). */
8764
+ device: z.string().regex(DEVICE_ID_PATTERN),
8765
+ /** Fixed by the way in (the kind of device in the registry), never read from the order. */
8766
+ source: z.enum(ORDER_SOURCES),
8767
+ applied_at: utcDatetime
8768
+ });
8769
+ /**
8770
+ * `payload` of `order.rejected` v1: an order whose signature checked out and that the bridge did
8771
+ * not apply, with the code the creator sees (never `UNKNOWN_DEVICE`). It carries no `params`: a
8772
+ * refusal is a fact, not a second copy of the order.
8773
+ */
8774
+ const OrderRejectedPayloadSchema = z.strictObject({
8775
+ order: UlidSchema,
8776
+ /** `order.<type>/v<version>`. */
8777
+ t: z.string().regex(ORDER_TAG),
8778
+ device: z.string().regex(DEVICE_ID_PATTERN),
8779
+ code: z.string().regex(/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$/),
8780
+ source: z.enum(ORDER_SOURCES),
8781
+ rejected_at: utcDatetime
8782
+ });
8783
+ /**
8784
+ * A link or a QR code for one more browser or phone: the rendezvous and the secret the device drew,
8785
+ * and the device's own signature of the authorization the bridge will write `device.paired` on
8786
+ * (`offer_sig`, over the `pair.offer` record of the kind of device, this bridge, `x` and `expires_at`).
8787
+ */
8788
+ const PairOfferLinkParamsSchema = z.strictObject({
8789
+ kind: z.enum(["link", "phone"]),
8790
+ x: z.string().length(22).refine(isCanonicalBase64url),
8791
+ s: z.string().length(22).refine(isCanonicalBase64url),
8792
+ expires_at: utcDatetime,
8793
+ offer_sig: Ed25519SignatureSchema
8794
+ });
8795
+ /** The short code of another machine: an address of 8 symbols and a password of 6. */
8796
+ const PairOfferCodeParamsSchema = z.strictObject({
8797
+ kind: z.literal("machine"),
8798
+ x: z.string().regex(/^[0-9A-HJKMNP-TV-Z]{8}$/),
8799
+ w: z.string().regex(/^[0-9A-HJKMNP-TV-Z]{6}$/),
8800
+ expires_at: utcDatetime,
8801
+ offer_sig: Ed25519SignatureSchema
8802
+ });
8803
+ z.union([PairOfferLinkParamsSchema, PairOfferCodeParamsSchema]);
8804
+ z.strictObject({ heads: z.record(z.string().regex(BRIDGE_ID_PATTERN), z.string().regex(TRUST_EVENT_HASH_PATTERN)).refine((heads) => Object.keys(heads).length <= 64, { message: "at most 64 writers" }) });
8805
+ z.strictObject({ role: z.string().max(120) });
8806
+ z.strictObject({});
8807
+ /** The kinds of connection a secret belongs to, and the shape of its name (`forge.github`, `data.crm`). */
8808
+ const SECRET_NAME_PATTERN = /^(?:forge|data)\.[a-z0-9][a-z0-9._-]{0,63}$/;
8809
+ /** The longest sealed box of a secret, as text: the secret in UTF-8 (4 bytes a character), its envelope and the box's own. */
8810
+ const SEALED_SECRET_MAX_CHARS = Math.ceil(262784 / 3) + 80;
8811
+ z.strictObject({
8812
+ name: z.string().regex(SECRET_NAME_PATTERN),
8813
+ sealed: z.string().min(1).max(SEALED_SECRET_MAX_CHARS).regex(/^[A-Za-z0-9_-]+$/)
8814
+ });
8815
+ z.strictObject({ ranks: z.record(UlidSchema, z.int().min(1).max(9999)).refine((all) => Object.keys(all).length <= 64, { message: "at most 64 products" }) });
8816
+ /**
8817
+ * The creator removes a device (a phone, a browser or a machine, possibly the one that gives the
8818
+ * order). The device signs the `device.revoked` authorization the bridge will write on its
8819
+ * authority (over the record of the removed device, the head of its chain, the date and the
8820
+ * author, which is the device that gives the order), so that the journal proves who removed what
8821
+ * without the bridge's word. `chain_head` is the last event of a removed machine's chain the author
8822
+ * saw in a product's journal (each journal has its own chain), null for a phone or a browser.
8823
+ * `authorizations` holds one signature per product the author saw the machine in, and `*` for any
8824
+ * other product (always with a null head). The order is for the bridge that applies it, which
8825
+ * writes the removal in every product it serves.
8826
+ */
8827
+ const RevocationAuthorizationSchema = z.strictObject({
8828
+ chain_head: z.string().regex(TRUST_EVENT_HASH_PATTERN).nullable(),
8829
+ sig: Ed25519SignatureSchema
8830
+ });
8831
+ z.strictObject({
8832
+ device: z.string().regex(PAIRED_DEVICE_ID_PATTERN),
8833
+ issued_at: utcDatetime,
8834
+ authorizations: z.record(z.string().refine((key) => key === "*" || ULID_PATTERN.test(key), { message: "a product identifier or *" }), RevocationAuthorizationSchema).refine((all) => "*" in all && all["*"]?.chain_head === null, { message: "the authorization for any product has no chain head" }).refine((all) => Object.keys(all).length <= 65, { message: "at most 64 products" })
8835
+ });
6929
8836
  //#endregion
6930
8837
  //#region ../../packages/protocol/dist/product-key.js
6931
8838
  /**
@@ -6936,12 +8843,21 @@ const MethodMigratedPayloadSchema = z.strictObject({
6936
8843
  * written: by `product.key_created` in the journal, and in each encrypted value.
6937
8844
  */
6938
8845
  /** Event types of the product key, each at version 1. */
6939
- const PRODUCT_KEY_EVENT_TYPES = {
6940
- /** The product key was created: its `key_id`, signed by the machine that created it. */
6941
- keyCreated: "product.key_created" };
8846
+ const PRODUCT_KEY_EVENT_TYPES = {
8847
+ /** The product key was created: its `key_id`, signed by the machine that created it. */
8848
+ keyCreated: "product.key_created",
8849
+ /**
8850
+ * The product key was renewed: a new key follows the one named by `previous_key_id`. The old key
8851
+ * stays valid for reading everything written before; only new content is written under the new
8852
+ * one. Each trusted machine gets the new key in a box sealed for its own encryption key.
8853
+ */
8854
+ keyRotated: "product.key_rotated"
8855
+ };
6942
8856
  /** `pk:` then the first 16 base64url characters of the key's `apkeyid1` subkey (`crypto_kdf`). */
6943
8857
  const KEY_ID_PATTERN = /^pk:[A-Za-z0-9_-]{16}$/;
6944
8858
  const KeyIdSchema = z.string().regex(KEY_ID_PATTERN);
8859
+ /** 32 bytes in base64url: 43 characters, the last one carrying 4 bits only. */
8860
+ const PROOF_PATTERN = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
6945
8861
  /**
6946
8862
  * `payload` of `product.key_created`: the `key_id`, and the `proof` that its writer held the key
6947
8863
  * (keyed BLAKE2b of `agentplugged/key-proof/v1\0` + the product id, by the key's `approof1`
@@ -6949,7 +8865,33 @@ const KeyIdSchema = z.string().regex(KEY_ID_PATTERN);
6949
8865
  */
6950
8866
  const ProductKeyCreatedPayloadSchema = z.strictObject({
6951
8867
  key_id: KeyIdSchema,
6952
- proof: z.string().regex(/^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/)
8868
+ proof: z.string().regex(PROOF_PATTERN)
8869
+ });
8870
+ /** A trusted machine's identifier, the name of its box in a renewal. */
8871
+ const MACHINE_BOX_OWNER = /^machine:[A-Za-z0-9_-]{16}$/;
8872
+ /** A sealed box (libsodium `crypto_box_seal`) in base64url: at most 256 characters. */
8873
+ const KEY_BOX = z.string().regex(/^[A-Za-z0-9_-]{60,256}$/);
8874
+ /**
8875
+ * `payload` of `product.key_rotated`: the new `key_id`, the key it follows, the `proof` that the
8876
+ * writer held the new key (the same proof as the first announcement), and `boxes`: the new key
8877
+ * sealed, one box per trusted machine, for that machine's encryption key. No key is ever in clear.
8878
+ */
8879
+ const ProductKeyRotatedPayloadSchema = z.strictObject({
8880
+ key_id: KeyIdSchema,
8881
+ previous_key_id: KeyIdSchema,
8882
+ proof: z.string().regex(PROOF_PATTERN),
8883
+ /**
8884
+ * The proof that the writer held the key this one follows (made with that key, for this renewal
8885
+ * only): a machine that holds the previous key ignores a renewal that does not carry it.
8886
+ */
8887
+ chain_proof: z.string().regex(PROOF_PATTERN),
8888
+ /**
8889
+ * The signed order that asked for the renewal, when it came from a creator's device: the
8890
+ * `order.applied` of the same journal keeps that order. A renewal run again for the same order
8891
+ * is recognised by it and not made twice.
8892
+ */
8893
+ order: z.string().regex(/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/).optional(),
8894
+ boxes: z.record(z.string().regex(MACHINE_BOX_OWNER), KEY_BOX).refine((boxes) => Object.keys(boxes).length <= 64, { message: `at most 64 boxes` })
6953
8895
  });
6954
8896
  /** The algorithm of `payload_enc` and of the sensitive files: XChaCha20-Poly1305 (IETF). */
6955
8897
  const PAYLOAD_ENCRYPTION_ALG = "xchacha20poly1305";
@@ -6970,6 +8912,8 @@ const MAX_SENSITIVE_CONTENT_BYTES = 262144;
6970
8912
  const ENCRYPTED_FILE_PREFIX = "agentplugged-enc:1:";
6971
8913
  /** Prefix of an exported product key (`key export`). */
6972
8914
  const PRODUCT_KEY_EXPORT_PREFIX = "apk1:";
8915
+ /** Prefix of an export that holds several keys: the current one first, then the older ones. */
8916
+ const PRODUCT_KEYS_EXPORT_PREFIX = "apk2:";
6973
8917
  /** Why a path given to a sensitive file tool is refused. */
6974
8918
  const PATH_MESSAGE = "expected a relative path inside the area (`/` between names; no `..`, `.`, empty name, drive, reserved character or Windows device name; no name ending with a dot or a space)";
6975
8919
  /** A file name part: no control character, no character Windows refuses, not `.` or `..`. */
@@ -7043,8 +8987,8 @@ const READINESS_GESTURES = ["fix", "accept"];
7043
8987
  /** The option of the decision a blocking verdict opens by which the creator goes on all the same. */
7044
8988
  const READINESS_GO_ON = "go_on";
7045
8989
  const READINESS_FIX_FIRST = "fix_first";
7046
- const NO_CONTROL$2 = { message: "expected no control character" };
7047
- const lineText$2 = (max) => z.string().trim().min(1).max(max).refine((text) => !/[\r\n]/.test(text), { message: "expected a single line" }).refine((text) => !hasControlCharacter(text, false), NO_CONTROL$2);
8990
+ const NO_CONTROL$1 = { message: "expected no control character" };
8991
+ const lineText$2 = (max) => z.string().trim().min(1).max(max).refine((text) => !/[\r\n]/.test(text), { message: "expected a single line" }).refine((text) => !hasControlCharacter(text, false), NO_CONTROL$1);
7048
8992
  /**
7049
8993
  * One judged finding as the report gives it. `passages` are `<document>: <passage>`: the document
7050
8994
  * by its id or its file name, then a heading or an identifier (`EF-n`, `ENF-n`, `PU-n`, `AD-n`) it
@@ -7395,8 +9339,8 @@ const REGULATED_FIELDS = [
7395
9339
  "health",
7396
9340
  "finance"
7397
9341
  ];
7398
- const NO_CONTROL$1 = { message: "expected no control character" };
7399
- const lineText$1 = (max) => z.string().trim().min(1).max(max).refine((text) => !/[\r\n]/.test(text), { message: "expected a single line" }).refine((text) => !hasControlCharacter(text, false), NO_CONTROL$1);
9342
+ const NO_CONTROL = { message: "expected no control character" };
9343
+ const lineText$1 = (max) => z.string().trim().min(1).max(max).refine((text) => !/[\r\n]/.test(text), { message: "expected a single line" }).refine((text) => !hasControlCharacter(text, false), NO_CONTROL);
7400
9344
  /** A guest's identifier: `guest-` and a slug, so it is never mistaken for an agent of the team. */
7401
9345
  const GuestIdSchema = z.string().max(48).regex(/^guest-[a-z0-9]+(?:-[a-z0-9]+)*$/);
7402
9346
  /** A saved room's identifier: the name of its files. */
@@ -7777,109 +9721,30 @@ const TestsReviewedPayloadSchema = z.strictObject({
7777
9721
  commit: CommitIdSchema.optional(),
7778
9722
  score: z.number().int().min(0).max(100).optional(),
7779
9723
  grade: z.enum([
7780
- "A",
7781
- "B",
7782
- "C",
7783
- "D",
7784
- "F"
7785
- ]).optional(),
7786
- verdict: z.enum([
7787
- "sound",
7788
- "to_improve",
7789
- "to_fix",
7790
- "blocked"
7791
- ]).optional(),
7792
- criteria: z.array(z.strictObject({
7793
- criterion: z.enum(TEST_CRITERIA),
7794
- score: z.number().int().min(0).max(100)
7795
- })).max(TEST_CRITERIA.length).optional(),
7796
- findings: z.number().int().min(0).max(60).optional(),
7797
- mutation: z.strictObject({
7798
- ran: z.number().int().min(0).max(30),
7799
- killed: z.number().int().min(0).max(30),
7800
- survived: z.number().int().min(0).max(30)
7801
- }).optional(),
7802
- gaps: z.number().int().min(0).max(1e3)
7803
- });
7804
- //#endregion
7805
- //#region ../../packages/protocol/dist/trace.js
7806
- /**
7807
- * Traceability and deviations. Each requirement of the PRD (`EF-n`, `ENF-n`) is linked,
7808
- * at read time, to the stories that cite it, their pull requests, the tests that cite it and the
7809
- * deviations recorded on it: nothing of that link is ever written in a document. A deviation is a
7810
- * signed event of the journal: an agent (the QA reviewer) found the delivered work drifting from
7811
- * requirements a started story cites. Its id, like an item of deferred work's, is `V-` then the
7812
- * last 10 characters of the ULID of its event, never written in the `payload`, never reused.
7813
- */
7814
- /** Event types of the deviations, each at version 1. */
7815
- const DEVIATION_EVENT_TYPES = {
7816
- reported: "deviation.reported",
7817
- resolved: "deviation.resolved"
7818
- };
7819
- /** Where a requirement stands: to do, in development, in review, delivered, or deviating. */
7820
- const REQUIREMENT_STATUSES = [
7821
- "todo",
7822
- "in_development",
7823
- "in_review",
7824
- "delivered",
7825
- "deviation"
7826
- ];
7827
- /** The requirements traced: the functional and non-functional ones of the PRD. */
7828
- const TRACED_REQUIREMENT_PATTERN = /^(?:EF|ENF)-[1-9]\d*$/;
7829
- /** A traced requirement as typed (`ef-18`, with spaces): trimmed, in upper case, then checked. */
7830
- const TracedRequirementInputSchema = z.string().trim().toUpperCase().pipe(z.string().regex(TRACED_REQUIREMENT_PATTERN, { message: "expected EF-n or ENF-n, for example EF-18" }));
7831
- /** `V-` then the last 10 characters of the ULID of the event that reported the deviation. */
7832
- const DeviationIdSchema = z.string().regex(/^V-[0-9A-HJKMNP-TV-Z]{10}$/, { message: "expected V- then 10 characters, for example V-7M2K9QX4TB" });
7833
- /** An id as typed (`v-7m2k9qx4tb`, with spaces): trimmed and in upper case, then checked. */
7834
- const DeviationIdInputSchema = z.string().trim().toUpperCase().pipe(DeviationIdSchema);
7835
- /** The id of the deviation reported by the event `eventId`: stable, never renumbered. */
7836
- function deviationIdOf(eventId) {
7837
- return `V-${eventId.slice(-10)}`;
7838
- }
7839
- const NO_CONTROL = { message: "expected no control character" };
7840
- const SummarySchema = z.string().trim().min(1).max(200).refine((summary) => !/[\r\n]/.test(summary), { message: "expected a single line" }).refine((summary) => !hasControlCharacter(summary, false), NO_CONTROL);
7841
- const DetailSchema = z.string().trim().min(1).max(2e3).refine((detail) => !hasControlCharacter(detail, true), NO_CONTROL);
7842
- const ReasonSchema = z.string().trim().min(1).max(1e3).refine((reason) => !hasControlCharacter(reason, true), NO_CONTROL);
7843
- const RequirementIdSchema = z.string().regex(REQUIREMENT_ID_PATTERN, { message: "expected a requirement id (EF-n, ENF-n, PU-n, MS-n or AD-n)" });
7844
- const requirementsOf = (id) => z.array(id).min(1).max(20).refine((ids) => new Set(ids).size === ids.length, { message: "requirements must differ" });
7845
- /**
7846
- * `payload` of `deviation.reported`. The story is the envelope's (required); `pr` is the number of
7847
- * the story's pull request when the deviation was reported, set by the engine from the journal.
7848
- */
7849
- const DeviationReportedPayloadSchema = z.strictObject({
7850
- requirements: requirementsOf(RequirementIdSchema),
7851
- summary: SummarySchema,
7852
- detail: DetailSchema.optional(),
7853
- pr: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER).optional(),
7854
- /** The commit compared with the requirements. */
7855
- commit: CommitIdSchema.optional()
7856
- });
7857
- /** `payload` of `deviation.resolved`: the deviation, and how it was fixed or why it is lifted. */
7858
- const DeviationResolvedPayloadSchema = z.strictObject({
7859
- deviation: DeviationIdSchema,
7860
- reason: ReasonSchema
7861
- });
7862
- /** A requirement id as typed: in upper case, then checked. */
7863
- const RequirementInputSchema = z.string().trim().toUpperCase().pipe(RequirementIdSchema);
7864
- /** Input of `report_deviation`: the story brought to its canonical form. */
7865
- const ReportDeviationInputSchema = z.strictObject({
7866
- agent: AgentIdSchema.max(64),
7867
- story: StoryIdSchema.overwrite(canonicalStoryId),
7868
- requirements: requirementsOf(RequirementInputSchema),
7869
- summary: SummarySchema,
7870
- detail: DetailSchema.optional(),
7871
- commit: CommitIdSchema.optional()
7872
- });
7873
- /** Input of `lift_deviation`: the agent that reported it lifts it, with why. */
7874
- const ResolveDeviationInputSchema = z.strictObject({
7875
- agent: AgentIdSchema.max(64),
7876
- deviation: DeviationIdInputSchema,
7877
- reason: ReasonSchema
9724
+ "A",
9725
+ "B",
9726
+ "C",
9727
+ "D",
9728
+ "F"
9729
+ ]).optional(),
9730
+ verdict: z.enum([
9731
+ "sound",
9732
+ "to_improve",
9733
+ "to_fix",
9734
+ "blocked"
9735
+ ]).optional(),
9736
+ criteria: z.array(z.strictObject({
9737
+ criterion: z.enum(TEST_CRITERIA),
9738
+ score: z.number().int().min(0).max(100)
9739
+ })).max(TEST_CRITERIA.length).optional(),
9740
+ findings: z.number().int().min(0).max(60).optional(),
9741
+ mutation: z.strictObject({
9742
+ ran: z.number().int().min(0).max(30),
9743
+ killed: z.number().int().min(0).max(30),
9744
+ survived: z.number().int().min(0).max(30)
9745
+ }).optional(),
9746
+ gaps: z.number().int().min(0).max(1e3)
7878
9747
  });
7879
- /** The reason the creator gives at the terminal (`trace resolve`). */
7880
- const DeviationReasonSchema = ReasonSchema;
7881
- /** Input of `get_trace`: one requirement, or every one when absent. */
7882
- const GetTraceInputSchema = z.strictObject({ requirement: z.string().max(32).optional() });
7883
9748
  //#endregion
7884
9749
  //#region ../../packages/protocol/dist/usage.js
7885
9750
  /**
@@ -8030,6 +9895,26 @@ const EVENT_TYPE_REGISTRY = {
8030
9895
  version: currentVersionOf(AGENT_EVENT_TYPES.statusReported),
8031
9896
  payload: StatusReportedPayloadSchema
8032
9897
  },
9898
+ /** An instruction of the creator to an agent: their own words, encrypted with the product key. */
9899
+ [CHAIN_EVENT_TYPES.instructionGiven]: {
9900
+ version: 1,
9901
+ payload: InstructionGivenPayloadSchema,
9902
+ sensitive: true
9903
+ },
9904
+ /** The creator holds the whole team, then lets it go on. */
9905
+ [TEAM_EVENT_TYPES.paused]: {
9906
+ version: 1,
9907
+ payload: TeamPausedPayloadSchema
9908
+ },
9909
+ [TEAM_EVENT_TYPES.resumed]: {
9910
+ version: 1,
9911
+ payload: TeamResumedPayloadSchema
9912
+ },
9913
+ /** Noah's short note at the end of an exchange with the creator. */
9914
+ [EXCHANGE_EVENT_TYPES.summarized]: {
9915
+ version: 1,
9916
+ payload: ExchangeSummarizedPayloadSchema
9917
+ },
8033
9918
  [AGENT_EVENT_TYPES.progressReported]: {
8034
9919
  version: 1,
8035
9920
  payload: ProgressReportedPayloadSchema
@@ -8075,6 +9960,10 @@ const EVENT_TYPE_REGISTRY = {
8075
9960
  version: 1,
8076
9961
  payload: ProductKeyCreatedPayloadSchema
8077
9962
  },
9963
+ [PRODUCT_KEY_EVENT_TYPES.keyRotated]: {
9964
+ version: 1,
9965
+ payload: ProductKeyRotatedPayloadSchema
9966
+ },
8078
9967
  /**
8079
9968
  * The registry of trusted devices: a device paired, revoked, confirmed, the
8080
9969
  * root certified by the first phone, and an alert dismissed by the creator. Clear: every
@@ -8100,6 +9989,21 @@ const EVENT_TYPE_REGISTRY = {
8100
9989
  version: 1,
8101
9990
  payload: TrustDismissedPayloadSchema
8102
9991
  },
9992
+ /** The main bridge of the product, chosen by the creator. */
9993
+ [MACHINE_EVENT_TYPES.primaryChanged]: {
9994
+ version: 1,
9995
+ payload: PrimaryChangedPayloadSchema
9996
+ },
9997
+ /** An order a bridge applied, with the creator's signed order kept as the proof. */
9998
+ [ORDER_EVENT_TYPES.applied]: {
9999
+ version: 1,
10000
+ payload: OrderAppliedPayloadSchema
10001
+ },
10002
+ /** A signed order a bridge refused, with the code the creator saw. */
10003
+ [ORDER_EVENT_TYPES.rejected]: {
10004
+ version: 1,
10005
+ payload: OrderRejectedPayloadSchema
10006
+ },
8103
10007
  /** Reserved to tests: any payload. */
8104
10008
  [TEST_EVENT_TYPE]: {
8105
10009
  version: 1,
@@ -8310,6 +10214,26 @@ const EVENT_TYPE_REGISTRY = {
8310
10214
  version: currentVersionOf(ELICITATION_EVENT_TYPES.answered),
8311
10215
  payload: CheckpointElicitedPayloadSchema
8312
10216
  },
10217
+ /** The creator's answer carried by the signed order of a browser (the web view). */
10218
+ [WEB_ANSWER_EVENT_TYPES.answered]: {
10219
+ version: currentVersionOf(WEB_ANSWER_EVENT_TYPES.answered),
10220
+ payload: CheckpointWebAnsweredPayloadSchema
10221
+ },
10222
+ /** An answer from the web held for the undo window: the point stays open meanwhile. */
10223
+ [WEB_ANSWER_EVENT_TYPES.held]: {
10224
+ version: currentVersionOf(WEB_ANSWER_EVENT_TYPES.held),
10225
+ payload: CheckpointWebHeldPayloadSchema
10226
+ },
10227
+ /** A held answer taken back by the creator: the point was never answered. */
10228
+ [WEB_ANSWER_EVENT_TYPES.holdCancelled]: {
10229
+ version: 1,
10230
+ payload: CheckpointWebHoldCancelledPayloadSchema
10231
+ },
10232
+ /** A held answer the machine could not carry out: released, the point is open again and says why. */
10233
+ [WEB_ANSWER_EVENT_TYPES.holdFailed]: {
10234
+ version: 1,
10235
+ payload: CheckpointWebHoldFailedPayloadSchema
10236
+ },
8313
10237
  /** A story merged by the engine once the creator approved its merge checkpoint. */
8314
10238
  [MERGE_EVENT_TYPES.merged]: {
8315
10239
  version: currentVersionOf(MERGE_EVENT_TYPES.merged),
@@ -8417,6 +10341,7 @@ function isTestEventType(type) {
8417
10341
  * a sensitive domain (`salle.*`, `security.*`, `memory.*`), known yet or not.
8418
10342
  */
8419
10343
  function isSensitiveEventType(type) {
10344
+ if (isExtensionEventType(type)) return true;
8420
10345
  if (eventTypeSpec(type)?.sensitive === true) return true;
8421
10346
  const domain = type.split(".")[0] ?? "";
8422
10347
  return SENSITIVE_EVENT_DOMAINS.includes(domain);
@@ -8431,6 +10356,7 @@ function eventTypeSpec(type) {
8431
10356
  * undefined when it can read it.
8432
10357
  */
8433
10358
  function eventTypeUpgrade(type, version) {
10359
+ if (isExtensionEventType(type)) return void 0;
8434
10360
  const spec = eventTypeSpec(type);
8435
10361
  if (spec !== void 0 && version <= spec.version) return void 0;
8436
10362
  return {
@@ -8455,6 +10381,10 @@ function currentEvent(event) {
8455
10381
  ok: false,
8456
10382
  error: upgrade
8457
10383
  };
10384
+ if (isExtensionEventType(event.type)) return {
10385
+ ok: true,
10386
+ event
10387
+ };
8458
10388
  const spec = eventTypeSpec(event.type);
8459
10389
  let current = event;
8460
10390
  for (let version = event.v; version < spec.version; version++) {
@@ -8481,6 +10411,437 @@ function currentEvent(event) {
8481
10411
  event: current
8482
10412
  };
8483
10413
  }
10414
+ /** The request shape (without the identifier, the version and the product, which the file reading adds). */
10415
+ const FindRequestFields = {
10416
+ op: z.literal("find"),
10417
+ query: z.string().min(1).max(120)
10418
+ };
10419
+ /** A place in a document where the words are: the document, its section, the line and the words around. */
10420
+ const FindHitSchema = z.strictObject({
10421
+ /** The path from the product's folder, with `/`. */
10422
+ path: z.string().min(1).max(300),
10423
+ /** True for a page of the knowledge base (it opens in the knowledge base, not in the documents). */
10424
+ kb: z.literal(true).optional(),
10425
+ title: z.string().min(1).max(200),
10426
+ /** The heading of the section, when it has one. */
10427
+ heading: z.string().max(200).optional(),
10428
+ /** The line of the document where the words are, from 1. */
10429
+ line: z.int().min(1).max(1e7),
10430
+ /** The line itself, cut around the words. */
10431
+ snippet: z.string().min(1).max(240)
10432
+ });
10433
+ const FindDataSchema = z.strictObject({
10434
+ op: z.literal("find"),
10435
+ hits: z.array(FindHitSchema).max(8),
10436
+ /** True when more documents hold the words than the answer lists. */
10437
+ truncated: z.boolean(),
10438
+ /** True when some document could not be read in full: the answer may miss some. */
10439
+ incomplete: z.boolean().optional()
10440
+ });
10441
+ const KB_CLAIM_MAX_CHARS = 1200;
10442
+ /** The request shapes (without the identifier, the version and the product, which the file reading adds). */
10443
+ const KbListRequestFields = { op: z.literal("kb") };
10444
+ const KbAskRequestFields = {
10445
+ op: z.literal("ask"),
10446
+ /** Chosen by the device: asking again with it reads the same question's state, it never starts a second one. */
10447
+ ask: z.string().regex(ULID_PATTERN),
10448
+ question: z.string().min(1).max(800)
10449
+ };
10450
+ /** One page of the knowledge base. */
10451
+ const KbPageSchema = z.strictObject({
10452
+ /** The path from the product's folder, with `/`. */
10453
+ path: z.string().min(1).max(300),
10454
+ title: z.string().min(1).max(200),
10455
+ /** The first lines of the page, as written. */
10456
+ summary: z.string().max(300).optional(),
10457
+ /** The day the page says it was last updated (`2026-10-01`), when it says one. */
10458
+ updated: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
10459
+ /** The last story the page says it took in (`2-3`), when it says one. */
10460
+ story: z.string().regex(/^[1-9]\d{0,5}-[1-9]\d{0,5}$/).optional()
10461
+ });
10462
+ const KbListDataSchema = z.strictObject({
10463
+ op: z.literal("kb"),
10464
+ pages: z.array(KbPageSchema).max(200),
10465
+ /** True when the product holds more pages than the bridge lists. */
10466
+ truncated: z.boolean(),
10467
+ /** True when some page could not be read in full: the list may miss some. */
10468
+ incomplete: z.boolean().optional(),
10469
+ /** Whether the machine can answer a question: an AI tool it knows how to run is there. */
10470
+ askable: z.boolean()
10471
+ });
10472
+ /** A place of the base a statement stands on: the page, the section, the line and the words. */
10473
+ const KbCitationSchema = z.strictObject({
10474
+ path: z.string().min(1).max(300),
10475
+ /** The heading of the section the passage is in, when it has one. */
10476
+ heading: z.string().max(200).optional(),
10477
+ /** The line of the page where the quotation starts, from 1. */
10478
+ line: z.int().min(1).max(1e7),
10479
+ /** The words of the page, as they are written there. */
10480
+ quote: z.string().min(1).max(300)
10481
+ });
10482
+ const KbClaimSchema = z.strictObject({
10483
+ text: z.string().min(1).max(KB_CLAIM_MAX_CHARS),
10484
+ /** At least one: a statement without a source that holds never leaves the bridge. */
10485
+ citations: z.array(KbCitationSchema).min(1).max(4)
10486
+ });
10487
+ const KbCostSchema = z.strictObject({
10488
+ usd: z.number().min(0).max(1e6).optional(),
10489
+ basis: z.string().min(1).max(16),
10490
+ tokens_in: z.int().min(0).max(1e9).optional(),
10491
+ tokens_out: z.int().min(0).max(1e9).optional()
10492
+ });
10493
+ const KbAskDataSchema = z.strictObject({
10494
+ op: z.literal("ask"),
10495
+ ask: z.string().regex(ULID_PATTERN),
10496
+ /** `working`: ask again; `answered`: the answer is below; `failed`: `failure` says why. */
10497
+ state: z.enum([
10498
+ "working",
10499
+ "answered",
10500
+ "failed"
10501
+ ]),
10502
+ failure: z.string().min(1).max(32).optional(),
10503
+ answer: z.strictObject({
10504
+ claims: z.array(KbClaimSchema).max(12),
10505
+ /** True when nothing in the base answers the question (or nothing that was said held). */
10506
+ no_source: z.boolean(),
10507
+ /** How many statements the bridge removed because no citation of theirs held. */
10508
+ dropped: z.int().min(0).max(1e3)
10509
+ }).optional(),
10510
+ /** How many passages of the base the AI tool was given (0: it was not asked). */
10511
+ passages: z.int().min(0).max(100),
10512
+ cost: KbCostSchema.optional()
10513
+ });
10514
+ /** The local reference holding the last journal commit projected: never pushed. */
10515
+ const JOURNAL_PROJECTED_REF = "refs/agentplugged/projected";
10516
+ const IgnoredEventSchema = z.strictObject({
10517
+ id: z.string(),
10518
+ reason: z.enum([
10519
+ "unknown_signer",
10520
+ "bad_signature",
10521
+ "broken_chain",
10522
+ "wrong_product",
10523
+ "invalid",
10524
+ "key_conflict",
10525
+ "product_conflict",
10526
+ "test_type",
10527
+ "checkpoint_id_collision",
10528
+ "not_human",
10529
+ "already_resolved",
10530
+ "unendorsed_human",
10531
+ "checkpoint_unknown",
10532
+ "decrypt_failed",
10533
+ "key_proof_invalid",
10534
+ "sensitive_in_clear",
10535
+ "unexpected_encryption",
10536
+ "unsupported_alg",
10537
+ "untrusted_authorization",
10538
+ "device_revoked",
10539
+ "dismissed",
10540
+ "not_reopenable"
10541
+ ])
10542
+ });
10543
+ /** Two valid events of one device name the same `prev` (two clones on one machine): both kept. */
10544
+ const ChainForkWarningSchema = z.strictObject({
10545
+ code: z.literal("chain_fork"),
10546
+ device: z.string(),
10547
+ prev: z.string().nullable(),
10548
+ ids: z.array(z.string())
10549
+ });
10550
+ /**
10551
+ * Two `checkpoint.opened` events give the same checkpoint id: `ids[0]` opened it, the others are
10552
+ * ignored (`checkpoint_id_collision`).
10553
+ */
10554
+ const CheckpointIdCollisionWarningSchema = z.strictObject({
10555
+ code: z.literal("checkpoint_id_collision"),
10556
+ checkpoint: z.string(),
10557
+ ids: z.array(z.string())
10558
+ });
10559
+ /**
10560
+ * A signed `checkpoint.opened` asks a weaker recipient or confirmation than the rules give: the
10561
+ * projection applies the rules' values, and says so. `fields` names what was raised.
10562
+ */
10563
+ const CheckpointWeakPayloadWarningSchema = z.strictObject({
10564
+ code: z.literal("checkpoint_weak_payload"),
10565
+ checkpoint: z.string(),
10566
+ id: z.string(),
10567
+ fields: z.array(z.enum(["recipient_role", "confirmation"]))
10568
+ });
10569
+ /**
10570
+ * A `high` risk checkpoint answered with the `locale` assurance only (no `presence` credential):
10571
+ * accepted, and shown as such.
10572
+ */
10573
+ const LocaleOnHighRiskWarningSchema = z.strictObject({
10574
+ code: z.literal("locale_on_high_risk"),
10575
+ checkpoint: z.string(),
10576
+ id: z.string()
10577
+ });
10578
+ z.strictObject({
10579
+ code: z.literal("sensitive_unreadable"),
10580
+ count: z.number().int().min(1)
10581
+ });
10582
+ /**
10583
+ * Warnings drawn from the journal alone, the same on every machine. Machine-local ones (a history
10584
+ * rewritten since this machine's last projection, sensitive content this machine cannot read)
10585
+ * are never written to `state.json`.
10586
+ */
10587
+ const ProjectionWarningSchema = z.discriminatedUnion("code", [
10588
+ ChainForkWarningSchema,
10589
+ CheckpointIdCollisionWarningSchema,
10590
+ CheckpointWeakPayloadWarningSchema,
10591
+ LocaleOnHighRiskWarningSchema
10592
+ ]);
10593
+ const ProjectedDeviceSchema = z.strictObject({
10594
+ device: z.string(),
10595
+ public_key: z.string()
10596
+ });
10597
+ /**
10598
+ * `state.json`: deterministic (two machines write the same bytes from the same journal, in
10599
+ * canonical JSON), `computed_at` being the date (`at`) of the last event applied in projection
10600
+ * order, never the clock.
10601
+ */
10602
+ const ProjectionStateSchema = z.strictObject({
10603
+ format: z.literal(1),
10604
+ /** The journal position projected (a commit id in Git), or null for an empty journal. */
10605
+ journal_head: z.string().nullable(),
10606
+ /** The `at` of the last event applied, in projection order; null when none was applied. */
10607
+ computed_at: z.string().nullable(),
10608
+ /** The product id of the journal's `product.created`, or null. */
10609
+ product: z.string().nullable(),
10610
+ /** The registered machines, sorted by `device` (the creators are in `state.humans`). */
10611
+ devices: z.array(ProjectedDeviceSchema),
10612
+ /** Events applied. */
10613
+ applied: z.number().int().min(0),
10614
+ /** Events ignored, sorted by `id` then `reason`. */
10615
+ ignored: z.array(IgnoredEventSchema),
10616
+ warnings: z.array(ProjectionWarningSchema),
10617
+ /** What the reducers built. */
10618
+ state: z.record(z.string(), z.unknown())
10619
+ });
10620
+ //#endregion
10621
+ //#region ../../packages/protocol/dist/published-state.js
10622
+ /**
10623
+ * Where a view stands in a journal: `n` is the number of events the journal held (it never goes
10624
+ * down) and `heads` the head of each writer's chain.
10625
+ */
10626
+ const FeedCursorSchema = z.strictObject({
10627
+ n: z.number().int().min(0),
10628
+ heads: z.record(z.string().min(1).max(64), z.string().min(1).max(128))
10629
+ });
10630
+ /** The devices the journal trusts, as the views need them. */
10631
+ const PublishedTrustedDeviceSchema = z.strictObject({
10632
+ kind: z.enum([
10633
+ "machine",
10634
+ "human",
10635
+ "phone",
10636
+ "browser"
10637
+ ]),
10638
+ sig_key: z.string().min(1).max(128),
10639
+ enc_key: z.string().min(1).max(128).nullable(),
10640
+ provisional: z.boolean()
10641
+ });
10642
+ /**
10643
+ * Where the import of an earlier history stands: the events brought in out of those the journal
10644
+ * holds, and the totals of the product (the creator reads « 48 stories, 131 validations »).
10645
+ * `ignored` counts the events the journal holds that were refused as forged, unsigned or cut from
10646
+ * their chain: they are told, never counted among the validations.
10647
+ */
10648
+ const ImportInfoSchema = z.strictObject({
10649
+ state: z.enum([
10650
+ "none",
10651
+ "running",
10652
+ "done",
10653
+ "failed"
10654
+ ]),
10655
+ progress: z.strictObject({
10656
+ events_done: z.number().int().nonnegative(),
10657
+ events_total: z.number().int().nonnegative()
10658
+ }).optional(),
10659
+ stories: z.number().int().nonnegative().optional(),
10660
+ validations: z.number().int().nonnegative().optional(),
10661
+ ignored: z.number().int().nonnegative().optional(),
10662
+ /** Why the import stopped (a short code, never a received value); with `failed` only. */
10663
+ reason: z.string().max(100).optional()
10664
+ });
10665
+ /**
10666
+ * What the exit guard puts in place of a text it kept from leaving. A stable code, not a sentence:
10667
+ * a view recognises it and shows it in the creator's language.
10668
+ */
10669
+ const REDACTED_SECRET = "redacted:secret";
10670
+ /**
10671
+ * What the main bridge publishes of a product. The projection is the one `get_state` returns; the
10672
+ * trust registry is the journal's (never the relay's list); the machines say which bridge is
10673
+ * main; the import says where the import of an earlier history stands.
10674
+ */
10675
+ const PublishedStateSchema = z.strictObject({
10676
+ projection: ProjectionStateSchema,
10677
+ trust: z.strictObject({
10678
+ /** True while the journal's root differs from the pinned one: no order is accepted. */
10679
+ blocked: z.boolean(),
10680
+ devices: z.record(z.string().regex(DEVICE_ID_PATTERN), PublishedTrustedDeviceSchema),
10681
+ revoked: z.array(z.string().regex(DEVICE_ID_PATTERN))
10682
+ }),
10683
+ machines: z.strictObject({
10684
+ primary: z.string().regex(BRIDGE_ID_PATTERN).nullable(),
10685
+ /** True when nobody chose the main bridge: the engine elected it from the journal. */
10686
+ automatic: z.boolean()
10687
+ }),
10688
+ import: ImportInfoSchema
10689
+ });
10690
+ readTolerant(z.strictObject({
10691
+ current: z.enum(PHASE_IDS).nullable(),
10692
+ since: z.string().nullable(),
10693
+ phases: z.array(z.strictObject({
10694
+ id: z.enum(PHASE_IDS),
10695
+ status: z.enum([
10696
+ "done",
10697
+ "in_progress",
10698
+ "in_review",
10699
+ "upcoming",
10700
+ "skipped"
10701
+ ])
10702
+ })).max(PHASE_IDS.length)
10703
+ }));
10704
+ /**
10705
+ * What a story's file says, as the main bridge read it from the product's working copy (the
10706
+ * `story_docs` key of `projection.state` of the published state, set only for the files the
10707
+ * bridge could read and that follow the story contract): the title, the owner, the requirements
10708
+ * it covers, the stories it depends on, its branch and budget, its intent and its acceptance
10709
+ * criteria in order. The state's `state` record is open, so a view that does not know this key
10710
+ * still reads the rest; a view that does reads each entry with this schema and leaves out the
10711
+ * ones that do not fit.
10712
+ */
10713
+ const PublishedStoryDocSchema = z.strictObject({
10714
+ title: z.string().min(1).max(400),
10715
+ owner: z.string().min(1).max(64).optional(),
10716
+ epic: z.string().min(1).max(64).optional(),
10717
+ requirements: z.array(z.string().min(1).max(64)).max(40),
10718
+ depends_on: z.array(z.string().min(1).max(64)).max(40),
10719
+ branch: z.string().min(1).max(400).optional(),
10720
+ budget_usd: z.number().finite().nonnegative().optional(),
10721
+ intent: z.string().max(1200),
10722
+ criteria: z.array(z.string().max(400)).max(40)
10723
+ });
10724
+ readTolerant(PublishedStoryDocSchema);
10725
+ z.record(z.string().regex(/^[1-9]\d{0,5}-[1-9]\d{0,5}$/), PublishedStoryDocSchema);
10726
+ const PATH_SEGMENT_MAX = 200;
10727
+ const PATH_DEPTH_MAX = 16;
10728
+ const FEED_OPS_MAX = 2e4;
10729
+ /** One step of a change: set the value at a path, or remove it. */
10730
+ const FeedOpSchema = z.discriminatedUnion("op", [z.strictObject({
10731
+ op: z.literal("set"),
10732
+ path: z.array(z.string().max(PATH_SEGMENT_MAX)).min(1).max(PATH_DEPTH_MAX),
10733
+ value: z.unknown()
10734
+ }), z.strictObject({
10735
+ op: z.literal("remove"),
10736
+ path: z.array(z.string().max(PATH_SEGMENT_MAX)).min(1).max(PATH_DEPTH_MAX)
10737
+ })]);
10738
+ const FeedSnapshotSchema = z.strictObject({
10739
+ kind: z.literal("snapshot"),
10740
+ schema: z.number().int().positive(),
10741
+ product: z.string().regex(ULID_PATTERN),
10742
+ cursor: FeedCursorSchema,
10743
+ state: PublishedStateSchema
10744
+ });
10745
+ const FeedChangeSchema = z.strictObject({
10746
+ kind: z.literal("change"),
10747
+ schema: z.number().int().positive(),
10748
+ product: z.string().regex(ULID_PATTERN),
10749
+ from_cursor: FeedCursorSchema,
10750
+ to_cursor: FeedCursorSchema,
10751
+ ops: z.array(FeedOpSchema).max(FEED_OPS_MAX)
10752
+ });
10753
+ z.strictObject({
10754
+ /** The same for all the parts of one message. */
10755
+ id: z.string().regex(ULID_PATTERN),
10756
+ index: z.number().int().min(0).max(4095),
10757
+ count: z.number().int().min(1).max(4096),
10758
+ /** SHA-256 of the whole content, base64url (43 characters). */
10759
+ digest: z.string().length(43).refine(isCanonicalBase64url),
10760
+ data: z.string().min(1).max(262144).refine(isCanonicalBase64url)
10761
+ });
10762
+ /** The bridge's presence: signed (as the message that carries it) and dated. */
10763
+ const FeedBeatSchema = z.strictObject({
10764
+ bridge: z.string().regex(BRIDGE_ID_PATTERN),
10765
+ /** Seconds since the epoch, on the bridge's clock. */
10766
+ at: z.number().int().positive(),
10767
+ bridge_version: SemverSchema,
10768
+ protocol_version: z.number().int().positive(),
10769
+ schema: z.number().int().positive(),
10770
+ /** The last sequence number this bridge gave to a message for this view. */
10771
+ last_seq: z.number().int().min(0),
10772
+ /** The products this bridge publishes, and the cursor of each. */
10773
+ publishes: z.array(z.strictObject({
10774
+ product: z.string().regex(ULID_PATTERN),
10775
+ cursor: FeedCursorSchema
10776
+ })).max(64),
10777
+ report: MachineReportSchema.optional()
10778
+ });
10779
+ z.strictObject({
10780
+ schema: z.number().int().positive(),
10781
+ product: z.string().regex(ULID_PATTERN),
10782
+ cursor: FeedCursorSchema.nullable()
10783
+ });
10784
+ z.strictObject({
10785
+ product: z.string().regex(ULID_PATTERN),
10786
+ cursor: FeedCursorSchema
10787
+ });
10788
+ const FeedUpgradeRequiredSchema = z.strictObject({
10789
+ current: z.number().int().positive(),
10790
+ minimum: z.number().int().positive()
10791
+ });
10792
+ /**
10793
+ * What a bridge tells a device it just removed: the device it means, the product whose journal
10794
+ * holds the removal and the date. The device believes it only from the bridge it pinned when it was
10795
+ * linked (the message is signed by that bridge's key), never from the relay closing a connection.
10796
+ */
10797
+ const FeedDeviceRemovedSchema = z.strictObject({
10798
+ device: z.string().regex(DEVICE_ID_PATTERN),
10799
+ product: z.string().regex(ULID_PATTERN),
10800
+ removed_at: z.iso.datetime()
10801
+ });
10802
+ readTolerant(PublishedStateSchema);
10803
+ readTolerant(FeedSnapshotSchema);
10804
+ readTolerant(FeedChangeSchema);
10805
+ readTolerant(FeedBeatSchema);
10806
+ readTolerant(FeedUpgradeRequiredSchema);
10807
+ readTolerant(FeedDeviceRemovedSchema);
10808
+ /** The most bytes one `vault_put` or `vault_blob` frame carries (decoded). */
10809
+ const VAULT_CHUNK_BYTES = 196608;
10810
+ /** What the relay keeps apart: a full vault, or the changes after it. */
10811
+ const VAULT_KINDS = ["full", "delta"];
10812
+ const b64 = (bytes) => z.string().length(Math.ceil(bytes * 4 / 3)).refine(isCanonicalBase64url);
10813
+ /** The content key sealed for one device: an ephemeral X25519 key, a nonce and the sealed key. */
10814
+ const VaultKeyBoxSchema = z.strictObject({
10815
+ device: z.string().regex(DEVICE_ID_PATTERN),
10816
+ /** The sender's ephemeral X25519 public key (32 bytes). */
10817
+ eph: b64(32),
10818
+ /** XChaCha20-Poly1305 nonce (24 bytes). */
10819
+ nonce: b64(24),
10820
+ /** The content key (32 bytes) and the authentication tag (16 bytes). */
10821
+ box: b64(48)
10822
+ });
10823
+ z.strictObject({
10824
+ v: z.literal(1),
10825
+ product: z.string().regex(ULID_PATTERN),
10826
+ kind: z.enum(VAULT_KINDS),
10827
+ rev: z.number().int().positive(),
10828
+ base_rev: z.number().int().positive().nullable(),
10829
+ /** When the machine sealed the blob, ISO 8601. */
10830
+ at: z.iso.datetime(),
10831
+ /** The machine that sealed it, by its journal identifier. */
10832
+ bridge: z.string().regex(BRIDGE_ID_PATTERN),
10833
+ boxes: z.array(VaultKeyBoxSchema).max(64),
10834
+ /** XChaCha20-Poly1305 nonce of the content (24 bytes). */
10835
+ nonce: b64(24),
10836
+ /** The compressed, encrypted content. */
10837
+ body: z.string().min(1).refine(isCanonicalBase64url),
10838
+ /** Signature of the header and of the digest of `body`. */
10839
+ sig: z.string().length(86).refine(isCanonicalBase64url)
10840
+ });
10841
+ readTolerant(z.strictObject({
10842
+ content: z.discriminatedUnion("kind", [FeedSnapshotSchema, FeedChangeSchema]),
10843
+ report: MachineReportSchema.optional()
10844
+ }));
8484
10845
  //#endregion
8485
10846
  //#region ../../packages/protocol/dist/relay.js
8486
10847
  /**
@@ -8520,7 +10881,8 @@ const KEY_32 = z.string().length(43).refine(isCanonicalBase64url);
8520
10881
  */
8521
10882
  const RelayAttachDeclarationSchema = z.strictObject({
8522
10883
  device_id: RelayIdSchema,
8523
- kind: z.literal("bridge"),
10884
+ /** `bridge` for a machine attached during a rendezvous, `browser` for a browser registering itself. */
10885
+ kind: z.enum(["bridge", "browser"]),
8524
10886
  sig_key: KEY_32,
8525
10887
  enc_key: KEY_32,
8526
10888
  relay: z.string().min(1).max(253),
@@ -8607,15 +10969,84 @@ z.discriminatedUnion("type", [
8607
10969
  /** Opaque bytes in base64url. */
8608
10970
  data: z.string().min(1).max(base64urlLength(RENDEZVOUS_MAX_DATA_BYTES)).refine(isCanonicalBase64url)
8609
10971
  }),
10972
+ z.strictObject({
10973
+ type: z.literal("device_register"),
10974
+ declaration: RelayAttachDeclarationSchema,
10975
+ signature: SignatureSchema,
10976
+ credential: z.string().min(1).max(4096).optional()
10977
+ }),
10978
+ z.strictObject({
10979
+ type: z.literal("device_revoke"),
10980
+ sig_key: KEY_32.optional(),
10981
+ /** Instead of the key: the relay's own identifier of the device (for a device no journal knows). */
10982
+ device_id: RelayIdSchema.optional()
10983
+ }).refine((frame) => frame.sig_key === void 0 !== (frame.device_id === void 0), { message: "name the device by its key or by its identifier, not both" }),
10984
+ z.strictObject({
10985
+ type: z.literal("vault_put"),
10986
+ product: RelayIdSchema,
10987
+ kind: z.enum(VAULT_KINDS),
10988
+ /** Rises with every blob of the product. */
10989
+ rev: z.number().int().positive(),
10990
+ /** The revision a change follows; null for a full vault. */
10991
+ base_rev: z.number().int().positive().nullable(),
10992
+ index: z.number().int().min(0).max(255),
10993
+ count: z.number().int().min(1).max(256),
10994
+ data: z.string().min(1).max(base64urlLength(VAULT_CHUNK_BYTES)).refine(isCanonicalBase64url)
10995
+ }),
10996
+ z.strictObject({
10997
+ type: z.literal("vault_get"),
10998
+ product: RelayIdSchema
10999
+ }),
11000
+ z.strictObject({ type: z.literal("outbox_list") }),
11001
+ z.strictObject({
11002
+ type: z.literal("push_register"),
11003
+ platform: z.enum(PUSH_PLATFORMS),
11004
+ token: PushTokenSchema
11005
+ }),
11006
+ z.strictObject({ type: z.literal("push_unregister") }),
11007
+ z.strictObject({
11008
+ type: z.literal("notify"),
11009
+ ...PushNoticeSchema.shape
11010
+ }),
11011
+ z.strictObject({
11012
+ type: z.literal("notify_clear"),
11013
+ id: PushNoticeSchema.shape.id
11014
+ }),
11015
+ z.strictObject({
11016
+ type: z.literal("presence_report"),
11017
+ surface: z.enum(NOTIFY_SURFACES),
11018
+ active: z.boolean()
11019
+ }),
11020
+ z.strictObject({ type: z.literal("notify_prefs_get") }),
11021
+ z.strictObject({ type: z.literal("modules_get") }),
11022
+ z.strictObject({
11023
+ type: z.literal("notify_prefs_set"),
11024
+ prefs: NotifyPrefsSchema
11025
+ }),
11026
+ z.strictObject({
11027
+ type: z.literal("outbox_cancel"),
11028
+ id: RelayIdSchema
11029
+ }),
8610
11030
  z.strictObject({
8611
11031
  type: z.literal("rv_attach"),
8612
11032
  declaration: RelayAttachDeclarationSchema,
8613
- signature: SignatureSchema
11033
+ signature: SignatureSchema,
11034
+ /**
11035
+ * A bridge already attached to the account that vouches for ANOTHER machine's declaration
11036
+ * (the machine signed it itself): the identifier of the voucher on this relay.
11037
+ */
11038
+ voucher: RelayIdSchema.optional()
8614
11039
  })
8615
11040
  ]);
8616
11041
  const PresenceEntrySchema = z.strictObject({
8617
11042
  device_id: RelayIdSchema,
8618
11043
  kind: z.enum(RELAY_DEVICE_KINDS),
11044
+ /**
11045
+ * The public signing key of the device: a device of the account's journal is recognised by it (the
11046
+ * relay's identifiers mean nothing to a journal), and a device no journal knows by its key is shown
11047
+ * as not verified.
11048
+ */
11049
+ sig_key: KEY_32.optional(),
8619
11050
  online: z.boolean(),
8620
11051
  /** ISO 8601, `null` when the device never connected. */
8621
11052
  last_seen_at: z.string().nullable()
@@ -8671,33 +11102,104 @@ z.discriminatedUnion("type", [
8671
11102
  x: RendezvousIdSchema
8672
11103
  }),
8673
11104
  z.strictObject({
8674
- type: z.literal("rv_joined"),
8675
- x: RendezvousIdSchema,
8676
- peer: z.string().min(1).max(32),
8677
- open: z.boolean()
11105
+ type: z.literal("rv_joined"),
11106
+ x: RendezvousIdSchema,
11107
+ peer: z.string().min(1).max(32),
11108
+ open: z.boolean()
11109
+ }),
11110
+ z.strictObject({
11111
+ type: z.literal("rv_peer"),
11112
+ peer: z.string().min(1).max(32)
11113
+ }),
11114
+ z.strictObject({
11115
+ type: z.literal("rv_message"),
11116
+ id: RelayIdSchema,
11117
+ peer: z.string().min(1).max(32).optional(),
11118
+ data: z.string().min(1).max(base64urlLength(RENDEZVOUS_MAX_DATA_BYTES)).refine(isCanonicalBase64url)
11119
+ }),
11120
+ z.strictObject({
11121
+ type: z.literal("rv_sent"),
11122
+ id: RelayIdSchema,
11123
+ delivered: z.boolean()
11124
+ }),
11125
+ z.strictObject({
11126
+ type: z.literal("rv_attached"),
11127
+ device_id: RelayIdSchema
11128
+ }),
11129
+ z.strictObject({
11130
+ type: z.literal("device_registered"),
11131
+ device_id: RelayIdSchema
11132
+ }),
11133
+ z.strictObject({
11134
+ type: z.literal("device_revoked"),
11135
+ found: z.boolean(),
11136
+ /** The relay's identifier of the device it stopped serving; null when there was none. */
11137
+ device_id: RelayIdSchema.nullable()
11138
+ }),
11139
+ z.strictObject({
11140
+ type: z.literal("device_gone"),
11141
+ device_id: RelayIdSchema
11142
+ }),
11143
+ z.strictObject({
11144
+ type: z.literal("vault_stored"),
11145
+ product: RelayIdSchema,
11146
+ rev: z.number().int().positive()
11147
+ }),
11148
+ z.strictObject({
11149
+ type: z.literal("vault_blob"),
11150
+ product: RelayIdSchema,
11151
+ rev: z.number().int().positive(),
11152
+ base_rev: z.number().int().positive().nullable(),
11153
+ kind: z.enum(VAULT_KINDS),
11154
+ /** When the relay received it, ISO 8601. */
11155
+ stored_at: z.iso.datetime(),
11156
+ index: z.number().int().min(0).max(255),
11157
+ count: z.number().int().min(1).max(256),
11158
+ data: z.string().min(1).max(base64urlLength(VAULT_CHUNK_BYTES)).refine(isCanonicalBase64url)
11159
+ }),
11160
+ z.strictObject({
11161
+ type: z.literal("vault_end"),
11162
+ product: RelayIdSchema,
11163
+ blobs: z.number().int().min(0)
11164
+ }),
11165
+ z.strictObject({ type: z.literal("vault_cleared") }),
11166
+ z.strictObject({ type: z.literal("push_registered") }),
11167
+ z.strictObject({ type: z.literal("push_unregistered") }),
11168
+ z.strictObject({
11169
+ type: z.literal("notified"),
11170
+ id: PushNoticeSchema.shape.id,
11171
+ sent: z.number().int().min(0).max(1e3)
11172
+ }),
11173
+ z.strictObject({
11174
+ type: z.literal("modules"),
11175
+ modules: z.array(z.string().regex(EXTENSION_MODULE_PATTERN)).max(32)
8678
11176
  }),
8679
11177
  z.strictObject({
8680
- type: z.literal("rv_peer"),
8681
- peer: z.string().min(1).max(32)
11178
+ type: z.literal("notify_prefs"),
11179
+ prefs: NotifyPrefsSchema
8682
11180
  }),
8683
11181
  z.strictObject({
8684
- type: z.literal("rv_message"),
8685
- id: RelayIdSchema,
8686
- peer: z.string().min(1).max(32).optional(),
8687
- data: z.string().min(1).max(base64urlLength(RENDEZVOUS_MAX_DATA_BYTES)).refine(isCanonicalBase64url)
11182
+ type: z.literal("notice"),
11183
+ ...PushNoticeSchema.shape
8688
11184
  }),
8689
11185
  z.strictObject({
8690
- type: z.literal("rv_sent"),
8691
- id: RelayIdSchema,
8692
- delivered: z.boolean()
11186
+ type: z.literal("outbox"),
11187
+ items: z.array(z.strictObject({
11188
+ id: RelayIdSchema,
11189
+ to: RelayIdSchema,
11190
+ queued_at: z.iso.datetime(),
11191
+ expires_at: z.iso.datetime()
11192
+ })).max(500)
8693
11193
  }),
8694
11194
  z.strictObject({
8695
- type: z.literal("rv_attached"),
8696
- device_id: RelayIdSchema
11195
+ type: z.literal("outbox_cancelled"),
11196
+ id: RelayIdSchema,
11197
+ cancelled: z.boolean()
8697
11198
  })
8698
11199
  ]);
8699
11200
  //#endregion
8700
11201
  //#region ../../packages/protocol/dist/pairing.js
11202
+ const PRODUCT_ID = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
8701
11203
  /**
8702
11204
  * The messages of the pairing link. Generic on purpose
8703
11205
  *: schemas and codes only; the link's bytes and the boxes are in `packages/crypto`, the
@@ -8709,6 +11211,10 @@ z.discriminatedUnion("type", [
8709
11211
  * - `envelope` (bridge to browser): a message from peer to peer encrypted for the
8710
11212
  * browser's key and signed by the bridge: `link.welcome` then one `product.keys` per product;
8711
11213
  * - `refused` (bridge to browser): a code and nothing else; informative, not authenticated.
11214
+ *
11215
+ * A machine that joins with a short code does not hold a link's secret: both sides run a password
11216
+ * exchange first (`pake_*` messages), and the box key is drawn from the session key it gives. The
11217
+ * bridge then says who it is in `hello`; the machine answers with its `request`.
8712
11218
  */
8713
11219
  /** Codes of the pairing, said by the bridge (separate from `ErrorCode`, like the relay's). */
8714
11220
  const PAIR_ERROR_CODES = {
@@ -8716,22 +11222,50 @@ const PAIR_ERROR_CODES = {
8716
11222
  PAIRING_EXPIRED: "PAIRING_EXPIRED",
8717
11223
  PAIRING_INVALID_PROOF: "PAIRING_INVALID_PROOF",
8718
11224
  PAIRING_KEY_MISMATCH: "PAIRING_KEY_MISMATCH",
11225
+ /** A machine joined from a folder whose product the bridge that gave the code does not serve. */
11226
+ PAIRING_PRODUCT_UNKNOWN: "PAIRING_PRODUCT_UNKNOWN",
8719
11227
  PAIRING_WRONG_ACCOUNT: "PAIRING_WRONG_ACCOUNT"
8720
11228
  };
8721
11229
  /** The type labels of the messages (`t` of the signed ones, `type` of the boxes and messages). */
8722
11230
  const PAIR_MESSAGE_TYPES = {
8723
11231
  request: "link.request/v1",
8724
11232
  welcome: TRUST_SIGNATURE_TAGS.linkWelcome,
8725
- productKeys: "product.keys/v1"
11233
+ productKeys: "product.keys/v1",
11234
+ hello: "pair.hello/v1"
8726
11235
  };
8727
11236
  const key32 = z.string().length(43).refine(isCanonicalBase64url);
11237
+ const key22 = z.string().length(22).refine(isCanonicalBase64url);
8728
11238
  const label = z.string().min(1).max(40);
11239
+ /** The devices that answer a pairing: a browser, a phone or another machine. */
11240
+ const REQUESTER_DEVICE_ID_PATTERN = /^(?:browser|phone|machine):[A-Za-z0-9_-]{16}$/;
11241
+ const PairCodeIdSchema = z.string().regex(/^[0-9A-HJKMNP-TV-Z]{8}$/);
11242
+ const PairCodeSecretSchema = z.string().regex(/^[0-9A-HJKMNP-TV-Z]{6}$/);
11243
+ /** `pair.offer/v1` for a link or a QR: a linked device asks its bridge to open a rendezvous. */
11244
+ const PairLinkOfferOrderSchema = z.strictObject({
11245
+ t: z.literal(TRUST_SIGNATURE_TAGS.pairOffer),
11246
+ /** Which kind of device the link is for: another browser or a phone. */
11247
+ kind: z.enum(["link", "phone"]),
11248
+ /** The rendezvous identifier: 16 random bytes in base64url. */
11249
+ x: z.string().length(22).refine(isCanonicalBase64url),
11250
+ /** The link's secret: 16 random bytes in base64url. Travels inside the encrypted order only. */
11251
+ s: z.string().length(22).refine(isCanonicalBase64url)
11252
+ });
11253
+ /** `pair.offer/v1` for another machine: the short code, drawn by the linked device. */
11254
+ const PairCodeOfferOrderSchema = z.strictObject({
11255
+ t: z.literal(TRUST_SIGNATURE_TAGS.pairOffer),
11256
+ kind: z.literal("machine"),
11257
+ /** The address to meet at (8 symbols). */
11258
+ x: PairCodeIdSchema,
11259
+ /** The password (6 symbols). Travels inside the encrypted order only. */
11260
+ w: PairCodeSecretSchema
11261
+ });
11262
+ z.union([PairLinkOfferOrderSchema, PairCodeOfferOrderSchema]);
8729
11263
  z.strictObject({
8730
11264
  t: z.literal(PAIR_MESSAGE_TYPES.request),
8731
11265
  /** The account of the requester, as the relay knows it. */
8732
11266
  account: RelayIdSchema,
8733
11267
  browser: z.strictObject({
8734
- device: z.string().regex(/^browser:[A-Za-z0-9_-]{16}$/),
11268
+ device: z.string().regex(REQUESTER_DEVICE_ID_PATTERN),
8735
11269
  sig: DeviceSigKeySchema,
8736
11270
  enc: DeviceEncKeySchema
8737
11271
  }),
@@ -8739,7 +11273,29 @@ z.strictObject({
8739
11273
  declared: z.strictObject({
8740
11274
  type: label,
8741
11275
  os: label
8742
- })
11276
+ }),
11277
+ /**
11278
+ * The requester's signature recognising the creator's key as the root of the link (the root
11279
+ * the link carried): it ties that key to the account without any gesture. Optional: a requester
11280
+ * that does not sign it is linked all the same, only the key stays unattached.
11281
+ */
11282
+ root_certification: z.strictObject({
11283
+ issued_at: z.iso.datetime(),
11284
+ sig: Ed25519SignatureSchema
11285
+ }).optional(),
11286
+ /**
11287
+ * Only a machine joining with a short code: the product it serves and the declaration it
11288
+ * signed to be attached to the relay (the bridge that holds the rendezvous vouches for it).
11289
+ */
11290
+ machine: z.strictObject({
11291
+ product: z.string().regex(PRODUCT_ID),
11292
+ relay_device: RelayIdSchema,
11293
+ attach_expires_at: z.number().int().positive(),
11294
+ attach_sig: Ed25519SignatureSchema
11295
+ }).optional()
11296
+ }).refine((request) => request.browser.device.startsWith("machine:") === (request.machine !== void 0), {
11297
+ path: ["machine"],
11298
+ message: "a machine says what it serves, and only a machine does"
8743
11299
  });
8744
11300
  z.strictObject({
8745
11301
  t: z.literal(PAIR_MESSAGE_TYPES.welcome),
@@ -8753,9 +11309,11 @@ z.strictObject({
8753
11309
  enc: z.strictObject({
8754
11310
  alg: z.literal(DEVICE_ENC_ALG),
8755
11311
  key: key32
8756
- })
11312
+ }),
11313
+ /** The identifier the relay knows the bridge by: where the browser's messages to it go. */
11314
+ relay_device: z.string().regex(/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/).optional()
8757
11315
  }),
8758
- browser: z.string().regex(/^browser:[A-Za-z0-9_-]{16}$/),
11316
+ browser: z.string().regex(REQUESTER_DEVICE_ID_PATTERN),
8759
11317
  /** SHA-256 of the opened request, base64url. */
8760
11318
  request: key32,
8761
11319
  registry: z.array(z.strictObject({
@@ -8769,12 +11327,38 @@ z.strictObject({
8769
11327
  })).max(64),
8770
11328
  sig: Ed25519SignatureSchema
8771
11329
  });
11330
+ z.strictObject({
11331
+ t: z.literal(PAIR_MESSAGE_TYPES.hello),
11332
+ root: z.strictObject({
11333
+ device: z.string().regex(/^(machine|human):[A-Za-z0-9_-]{16}$/),
11334
+ key: key32
11335
+ }),
11336
+ bridge: z.strictObject({
11337
+ device: z.string().regex(/^machine:[A-Za-z0-9_-]{16}$/),
11338
+ sig: DeviceSigKeySchema,
11339
+ enc: z.strictObject({
11340
+ alg: z.literal(DEVICE_ENC_ALG),
11341
+ key: key32
11342
+ })
11343
+ }),
11344
+ /** The account of the bridge, which the machine joins. */
11345
+ account: RelayIdSchema,
11346
+ now: z.number().int().positive()
11347
+ });
8772
11348
  z.strictObject({
8773
11349
  t: z.literal(PAIR_MESSAGE_TYPES.productKeys),
8774
- product: z.string().regex(/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/),
11350
+ product: z.string().regex(PRODUCT_ID),
8775
11351
  /** The identifier of the key (`apkeyid1`): the browser checks it against the journal's events. */
8776
11352
  key_id: z.string().min(1).max(64),
8777
- key: key32
11353
+ key: key32,
11354
+ /**
11355
+ * The keys the product had before it was renewed, so that a device linked afterwards reads the
11356
+ * whole history. Each is checked against its own `key_id` by the receiver.
11357
+ */
11358
+ older: z.array(z.strictObject({
11359
+ key_id: z.string().min(1).max(64),
11360
+ key: key32
11361
+ })).max(32).optional()
8778
11362
  });
8779
11363
  z.discriminatedUnion("k", [
8780
11364
  z.strictObject({
@@ -8793,111 +11377,526 @@ z.discriminatedUnion("k", [
8793
11377
  ciphertext: z.string().min(1).max(65536)
8794
11378
  })
8795
11379
  }),
11380
+ z.strictObject({
11381
+ k: z.literal("pake_init"),
11382
+ nonce: key22,
11383
+ share: key32
11384
+ }),
11385
+ z.strictObject({
11386
+ k: z.literal("pake_reply"),
11387
+ share: key32
11388
+ }),
11389
+ z.strictObject({
11390
+ k: z.literal("pake_confirm"),
11391
+ mac: key32
11392
+ }),
11393
+ z.strictObject({
11394
+ k: z.literal("pake_confirm_b"),
11395
+ mac: key32
11396
+ }),
11397
+ z.strictObject({
11398
+ k: z.literal("hello"),
11399
+ nonce: z.string().min(1).max(64),
11400
+ ciphertext: z.string().min(1).max(65536)
11401
+ }),
8796
11402
  z.strictObject({
8797
11403
  k: z.literal("refused"),
8798
11404
  code: z.enum(Object.values(PAIR_ERROR_CODES))
8799
11405
  })
8800
11406
  ]);
8801
- /** The local reference holding the last journal commit projected: never pushed. */
8802
- const JOURNAL_PROJECTED_REF = "refs/agentplugged/projected";
8803
- const IgnoredEventSchema = z.strictObject({
8804
- id: z.string(),
8805
- reason: z.enum([
8806
- "unknown_signer",
8807
- "bad_signature",
8808
- "broken_chain",
8809
- "wrong_product",
8810
- "invalid",
8811
- "key_conflict",
8812
- "product_conflict",
8813
- "test_type",
8814
- "checkpoint_id_collision",
8815
- "not_human",
8816
- "already_resolved",
8817
- "unendorsed_human",
8818
- "checkpoint_unknown",
8819
- "decrypt_failed",
8820
- "key_proof_invalid",
8821
- "sensitive_in_clear",
8822
- "unexpected_encryption",
8823
- "unsupported_alg",
8824
- "untrusted_authorization",
8825
- "device_revoked",
8826
- "dismissed",
8827
- "not_reopenable"
8828
- ])
11407
+ //#endregion
11408
+ //#region ../../packages/protocol/dist/product-browse.js
11409
+ /**
11410
+ * Choosing where a product is made, by clicking: the shapes the request and the answer of the file
11411
+ * reading carry (`folders`). A device names a place the machine listed in its report and the steps
11412
+ * taken inside it, one folder name each, as the machine listed them; it never sends a path. The
11413
+ * machine resolves the steps itself, refuses whatever leaves the place (a link, a junction, a
11414
+ * separator, `..`), and lists only the folders a creator may put a product in: no hidden folder,
11415
+ * no folder of the system.
11416
+ */
11417
+ /** The request shape (without the identifier, the version and the product, which the file reading adds). */
11418
+ const FoldersRequestFields = {
11419
+ op: z.literal("folders"),
11420
+ /** The identifier of a place the machine listed. */
11421
+ location: z.string().regex(PRODUCT_LOCATION_ID_PATTERN),
11422
+ /** The folders gone into, from the place. */
11423
+ path: z.array(z.string().refine(isBrowseStep, { message: "a folder name, not a path" })).max(12).optional()
11424
+ };
11425
+ const FoldersDataSchema = z.strictObject({
11426
+ op: z.literal("folders"),
11427
+ location: z.string().regex(PRODUCT_LOCATION_ID_PATTERN),
11428
+ /** The steps the answer is about (the request's). */
11429
+ path: z.array(z.string().refine(isBrowseStep)).max(12),
11430
+ /** The folder as the creator reads it (the home folder written `~`). */
11431
+ label: z.string().min(1).max(1200),
11432
+ /** The folders inside it that can be gone into, by name. */
11433
+ folders: z.array(z.string().refine(isBrowseStep)).max(200),
11434
+ /** True when there are more folders than listed. */
11435
+ truncated: z.boolean()
8829
11436
  });
8830
- /** Two valid events of one device name the same `prev` (two clones on one machine): both kept. */
8831
- const ChainForkWarningSchema = z.strictObject({
8832
- code: z.literal("chain_fork"),
8833
- device: z.string(),
8834
- prev: z.string().nullable(),
8835
- ids: z.array(z.string())
11437
+ //#endregion
11438
+ //#region ../../packages/protocol/dist/voice-read.js
11439
+ /** The largest piece of audio of one request, in bytes of PCM (3 seconds); the envelope must hold it sealed. */
11440
+ const TRANSCRIBE_CHUNK_MAX_BYTES = 96e3;
11441
+ /** The longest text the machine gives back, in characters. */
11442
+ const TRANSCRIBE_TEXT_MAX_CHARS = 4e3;
11443
+ /** What the request carries on top of the file reading's own fields. */
11444
+ const TranscribeRequestFields = {
11445
+ op: z.literal("transcribe"),
11446
+ /**
11447
+ * `probe`: asks whether the machine can transcribe, with no audio. `audio`: one piece of a
11448
+ * phrase; the last one (`final`) makes the machine transcribe and answer with the text.
11449
+ */
11450
+ step: z.enum(["probe", "audio"]),
11451
+ /** The phrase the piece belongs to (audio only). */
11452
+ session: z.string().regex(ULID_PATTERN).optional(),
11453
+ /** The place of the piece in the phrase, from 0 (audio only). */
11454
+ seq: z.int().min(0).max(7).optional(),
11455
+ /** True on the last piece of the phrase (audio only). */
11456
+ final: z.boolean().optional(),
11457
+ /** The language to recognize, a BCP-47 tag (audio only). */
11458
+ language: z.string().regex(/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})?$/).optional(),
11459
+ /** The PCM of the piece, base64url (audio only). */
11460
+ audio: z.string().regex(/^[A-Za-z0-9_-]*$/).max(base64urlLength(TRANSCRIBE_CHUNK_MAX_BYTES)).optional()
11461
+ };
11462
+ const TranscribeDataSchema = z.strictObject({
11463
+ op: z.literal("transcribe"),
11464
+ /** True when the machine has a transcription command set up. */
11465
+ ready: z.boolean(),
11466
+ /** The words understood: only on the answer to the last piece of a phrase. */
11467
+ text: z.string().max(TRANSCRIBE_TEXT_MAX_CHARS).optional()
8836
11468
  });
11469
+ /** The longest path, in characters, and the longest one name of a branch. */
11470
+ const FILES_PATH_MAX = 1024;
8837
11471
  /**
8838
- * Two `checkpoint.opened` events give the same checkpoint id: `ids[0]` opened it, the others are
8839
- * ignored (`checkpoint_id_collision`).
11472
+ * Where a file is read: the working copy of the product as it is now (uncommitted work included),
11473
+ * a branch or a commit of its repository, or the working copy of the story that works on a branch.
8840
11474
  */
8841
- const CheckpointIdCollisionWarningSchema = z.strictObject({
8842
- code: z.literal("checkpoint_id_collision"),
8843
- checkpoint: z.string(),
8844
- ids: z.array(z.string())
11475
+ const FilesRefSchema = z.discriminatedUnion("kind", [
11476
+ z.strictObject({ kind: z.literal("worktree") }),
11477
+ z.strictObject({
11478
+ kind: z.literal("branch"),
11479
+ name: z.string().min(1).max(200)
11480
+ }),
11481
+ z.strictObject({
11482
+ kind: z.literal("commit"),
11483
+ sha: z.string().regex(/^[0-9a-f]{40,64}$/)
11484
+ }),
11485
+ z.strictObject({
11486
+ kind: z.literal("workspace"),
11487
+ branch: z.string().min(1).max(200)
11488
+ })
11489
+ ]);
11490
+ const RequestBase = {
11491
+ v: z.literal(1),
11492
+ /** Chosen by the asking device: the answer repeats it. */
11493
+ id: z.string().regex(ULID_PATTERN),
11494
+ product: z.string().regex(ULID_PATTERN)
11495
+ };
11496
+ z.discriminatedUnion("op", [
11497
+ z.strictObject({
11498
+ ...RequestBase,
11499
+ op: z.literal("tree"),
11500
+ ref: FilesRefSchema,
11501
+ path: z.string().max(FILES_PATH_MAX)
11502
+ }),
11503
+ z.strictObject({
11504
+ ...RequestBase,
11505
+ op: z.literal("file"),
11506
+ ref: FilesRefSchema,
11507
+ path: z.string().min(1).max(FILES_PATH_MAX)
11508
+ }),
11509
+ z.strictObject({
11510
+ ...RequestBase,
11511
+ op: z.literal("download"),
11512
+ ref: FilesRefSchema,
11513
+ path: z.string().min(1).max(FILES_PATH_MAX)
11514
+ }),
11515
+ z.strictObject({
11516
+ ...RequestBase,
11517
+ op: z.literal("refs")
11518
+ }),
11519
+ z.strictObject({
11520
+ ...RequestBase,
11521
+ op: z.literal("changes"),
11522
+ base: FilesRefSchema,
11523
+ head: FilesRefSchema
11524
+ }),
11525
+ z.strictObject({
11526
+ ...RequestBase,
11527
+ op: z.literal("live"),
11528
+ after: z.number().int().min(0).optional(),
11529
+ epoch: z.string().min(1).max(64).optional()
11530
+ }),
11531
+ z.strictObject({
11532
+ ...RequestBase,
11533
+ ...CodePullsRequestFields
11534
+ }),
11535
+ z.strictObject({
11536
+ ...RequestBase,
11537
+ ...CodePullRequestFields
11538
+ }),
11539
+ z.strictObject({
11540
+ ...RequestBase,
11541
+ ...CodeDiffRequestFields
11542
+ }),
11543
+ z.strictObject({
11544
+ ...RequestBase,
11545
+ ...CodeEditorRequestFields
11546
+ }),
11547
+ z.strictObject({
11548
+ ...RequestBase,
11549
+ ...DocsListRequestFields
11550
+ }),
11551
+ z.strictObject({
11552
+ ...RequestBase,
11553
+ ...DocsTraceRequestFields
11554
+ }),
11555
+ z.strictObject({
11556
+ ...RequestBase,
11557
+ ...DocsRetrosRequestFields
11558
+ }),
11559
+ z.strictObject({
11560
+ ...RequestBase,
11561
+ ...KbListRequestFields
11562
+ }),
11563
+ z.strictObject({
11564
+ ...RequestBase,
11565
+ ...KbAskRequestFields
11566
+ }),
11567
+ z.strictObject({
11568
+ ...RequestBase,
11569
+ ...CostsRequestFields
11570
+ }),
11571
+ z.strictObject({
11572
+ ...RequestBase,
11573
+ ...BriefingRequestFields
11574
+ }),
11575
+ z.strictObject({
11576
+ ...RequestBase,
11577
+ ...TranscribeRequestFields
11578
+ }),
11579
+ z.strictObject({
11580
+ ...RequestBase,
11581
+ ...FindRequestFields
11582
+ }),
11583
+ z.strictObject({
11584
+ ...RequestBase,
11585
+ ...FoldersRequestFields
11586
+ })
11587
+ ]);
11588
+ /** Why a request was not answered with data; stable codes the views translate. */
11589
+ const FilesErrorCodeSchema = z.enum([
11590
+ "NOT_FOUND",
11591
+ "OUTSIDE_PRODUCT",
11592
+ "PROTECTED",
11593
+ "TOO_LARGE",
11594
+ "WRONG_KIND",
11595
+ "BAD_REQUEST",
11596
+ "UNAVAILABLE",
11597
+ "BUSY",
11598
+ ...CODE_ERROR_CODES
11599
+ ]);
11600
+ const FilesEntrySchema = z.strictObject({
11601
+ name: z.string().min(1).max(255),
11602
+ kind: z.enum([
11603
+ "file",
11604
+ "dir",
11605
+ "link"
11606
+ ]),
11607
+ /** Bytes; absent for a folder. */
11608
+ size: z.number().int().min(0).optional(),
11609
+ /** True for a file the bridge never sends (secrets): the name is visible, the content is not. */
11610
+ protected: z.literal(true).optional()
11611
+ });
11612
+ const FilesBranchSchema = z.strictObject({
11613
+ name: z.string().min(1).max(200),
11614
+ sha: z.string().regex(/^[0-9a-f]{40,64}$/),
11615
+ /** True for the branch the product's folder is on now. */
11616
+ current: z.literal(true).optional(),
11617
+ /** True when a working copy of this branch exists on the machine (a story at work). */
11618
+ workspace: z.literal(true).optional()
11619
+ });
11620
+ const FilesChangeSchema = z.strictObject({
11621
+ status: z.enum([
11622
+ "added",
11623
+ "modified",
11624
+ "deleted",
11625
+ "renamed"
11626
+ ]),
11627
+ path: z.string().min(1).max(FILES_PATH_MAX),
11628
+ /** The old path of a renamed file. */
11629
+ from: z.string().min(1).max(FILES_PATH_MAX).optional(),
11630
+ protected: z.literal(true).optional()
8845
11631
  });
8846
11632
  /**
8847
- * A signed `checkpoint.opened` asks a weaker recipient or confirmation than the rules give: the
8848
- * projection applies the rules' values, and says so. `fields` names what was raised.
8849
- */
8850
- const CheckpointWeakPayloadWarningSchema = z.strictObject({
8851
- code: z.literal("checkpoint_weak_payload"),
8852
- checkpoint: z.string(),
8853
- id: z.string(),
8854
- fields: z.array(z.enum(["recipient_role", "confirmation"]))
11633
+ * One line of what an agent does. `kind` is a stable code; `text` is plain text the view never
11634
+ * interprets: what the agent said (`say`), the name of a tool it called (`tool`, `tool_ok`,
11635
+ * `tool_failed`, `denied`) or the outcome code of the run (`end`).
11636
+ *
11637
+ * Three kinds tell what a call does to the machine: `read` and `edit` carry the path of the file,
11638
+ * relative to the agent's working folder, and `command` carries the command; a command's issue
11639
+ * (`command_ok`, `command_failed`) has no text and points to its command by `of`. A path that
11640
+ * leaves the working folder is never told: the line has `outside` and no text. `protected` marks
11641
+ * a file whose content the bridge never shows. `link` says where to read what an `edit` changed:
11642
+ * the same references and paths as the file reading (`file`, `changes`). The results of a tool,
11643
+ * and the content of files, are never in a line.
11644
+ *
11645
+ * In a conversation with Noah one more kind appears: `creator`, the creator's own message (plain
11646
+ * text, so that their other devices see it). What Noah says is a `say` of the agent `noah`:
11647
+ * untrusted text, like any agent's.
11648
+ */
11649
+ const LiveLineSchema = z.strictObject({
11650
+ seq: z.number().int().min(1),
11651
+ /** Milliseconds since the epoch, on the bridge's clock. */
11652
+ at: z.number().int().min(0),
11653
+ agent: z.string().min(1).max(64),
11654
+ kind: z.enum([
11655
+ "start",
11656
+ "say",
11657
+ "tool",
11658
+ "tool_ok",
11659
+ "tool_failed",
11660
+ "denied",
11661
+ "end",
11662
+ "read",
11663
+ "edit",
11664
+ "command",
11665
+ "command_ok",
11666
+ "command_failed",
11667
+ "creator"
11668
+ ]),
11669
+ text: z.string().max(2e3).optional(),
11670
+ /** For a command's issue: the `seq` of the line of the command. */
11671
+ of: z.number().int().min(1).optional(),
11672
+ /** The path leaves the agent's working folder: it is not told. */
11673
+ outside: z.literal(true).optional(),
11674
+ /** The file is a secret's: its name is told, its content is never shown. */
11675
+ protected: z.literal(true).optional(),
11676
+ /** The line comes from a session the creator opened in the tool's own terminal. */
11677
+ terminal: z.literal(true).optional(),
11678
+ /** For a change: where its content and its difference can be read. */
11679
+ link: z.strictObject({
11680
+ ref: FilesRefSchema,
11681
+ path: z.string().min(1).max(FILES_PATH_MAX)
11682
+ }).optional()
8855
11683
  });
8856
11684
  /**
8857
- * A `high` risk checkpoint answered with the `locale` assurance only (no `presence` credential):
8858
- * accepted, and shown as such.
11685
+ * Whether the sessions the creator opens in a tool's own terminal can be followed: `listening`
11686
+ * (the tool's hook is in the repository), `hook_missing` (it is not: `sync` writes it),
11687
+ * `unsupported` (the tool has no such hook yet). A stable code the views tell in the creator's language.
8859
11688
  */
8860
- const LocaleOnHighRiskWarningSchema = z.strictObject({
8861
- code: z.literal("locale_on_high_risk"),
8862
- checkpoint: z.string(),
8863
- id: z.string()
8864
- });
8865
- z.strictObject({
8866
- code: z.literal("sensitive_unreadable"),
8867
- count: z.number().int().min(1)
8868
- });
11689
+ const LiveTerminalStateSchema = z.enum([
11690
+ "listening",
11691
+ "hook_missing",
11692
+ "unsupported"
11693
+ ]);
11694
+ /** Where the team of the product stands, as a code the views tell in the creator's language. */
11695
+ const LiveChainStateSchema = z.enum([
11696
+ "none",
11697
+ "running",
11698
+ "waiting",
11699
+ "paused",
11700
+ "stopped"
11701
+ ]);
11702
+ /** What an answer carries, by operation. */
11703
+ const FilesDataSchema = z.discriminatedUnion("op", [
11704
+ CodePullsDataSchema,
11705
+ CodePullDataSchema,
11706
+ CodeDiffDataSchema,
11707
+ CodeEditorDataSchema,
11708
+ DocsListDataSchema,
11709
+ DocsTraceDataSchema,
11710
+ DocsRetrosDataSchema,
11711
+ KbListDataSchema,
11712
+ KbAskDataSchema,
11713
+ FindDataSchema,
11714
+ CostsDataSchema,
11715
+ BriefingDataSchema,
11716
+ TranscribeDataSchema,
11717
+ FoldersDataSchema,
11718
+ z.strictObject({
11719
+ op: z.literal("tree"),
11720
+ path: z.string().max(FILES_PATH_MAX),
11721
+ entries: z.array(FilesEntrySchema).max(5e3),
11722
+ /** True when the folder holds more than the bridge lists. */
11723
+ truncated: z.boolean()
11724
+ }),
11725
+ z.strictObject({
11726
+ op: z.literal("file"),
11727
+ path: z.string().min(1).max(FILES_PATH_MAX),
11728
+ size: z.number().int().min(0),
11729
+ /** `text`: shown; `binary`: only offered for download. */
11730
+ content: z.enum(["text", "binary"]),
11731
+ /** True when the file can be asked for as bytes to save (it is not larger than the bridge sends). */
11732
+ downloadable: z.boolean(),
11733
+ /** The text, with each line that looked like a secret replaced by `redacted:secret`. */
11734
+ text: z.string().optional(),
11735
+ /** How many lines were replaced. */
11736
+ masked: z.number().int().min(0)
11737
+ }),
11738
+ z.strictObject({
11739
+ op: z.literal("download"),
11740
+ path: z.string().min(1).max(FILES_PATH_MAX),
11741
+ size: z.number().int().min(0),
11742
+ /** The bytes, base64url. */
11743
+ bytes: z.string()
11744
+ }),
11745
+ z.strictObject({
11746
+ op: z.literal("refs"),
11747
+ branches: z.array(FilesBranchSchema).max(500),
11748
+ truncated: z.boolean()
11749
+ }),
11750
+ z.strictObject({
11751
+ op: z.literal("changes"),
11752
+ files: z.array(FilesChangeSchema).max(5e3),
11753
+ truncated: z.boolean()
11754
+ }),
11755
+ z.strictObject({
11756
+ op: z.literal("live"),
11757
+ /** Tells one run of the bridge's memory from another: send it back with `next`. */
11758
+ epoch: z.string().min(1).max(64),
11759
+ /** True when the cursor asked with was not this bridge's: the lines start again from the oldest kept. */
11760
+ reset: z.boolean(),
11761
+ /** How many lines were lost between the cursor and the first line below (memory is bounded). */
11762
+ dropped: z.number().int().min(0),
11763
+ /** The cursor to ask with next time. */
11764
+ next: z.number().int().min(0),
11765
+ /** True when more lines wait: ask again at once. */
11766
+ more: z.boolean(),
11767
+ chain: LiveChainStateSchema,
11768
+ /**
11769
+ * Present while the creator holds the whole team: `paused` (the agents finish their step and
11770
+ * start nothing) or `stopped` (the emergency stop cut what the bridge started).
11771
+ */
11772
+ team: z.enum(["paused", "stopped"]).optional(),
11773
+ /** Present while notices wait for Noah (a validation he has not read yet) and why. */
11774
+ notices: LiveNoticesSchema.optional(),
11775
+ /** The agent that works now, or null. */
11776
+ working: z.strictObject({
11777
+ agent: z.string().min(1).max(64),
11778
+ story: z.string().min(1).max(32).optional(),
11779
+ tool: z.string().min(1).max(32)
11780
+ }).nullable(),
11781
+ lines: z.array(LiveLineSchema).max(100),
11782
+ /** For each tool, whether its sessions opened in the creator's own terminal can be followed. */
11783
+ terminal: z.array(z.strictObject({
11784
+ tool: z.enum([
11785
+ "claude-code",
11786
+ "codex",
11787
+ "antigravity",
11788
+ "cursor"
11789
+ ]),
11790
+ state: LiveTerminalStateSchema
11791
+ })).max(8).optional()
11792
+ })
11793
+ ]);
11794
+ const FilesResponseSchema = z.discriminatedUnion("ok", [z.strictObject({
11795
+ v: z.literal(1),
11796
+ id: z.string().regex(ULID_PATTERN),
11797
+ product: z.string().regex(ULID_PATTERN),
11798
+ ok: z.literal(true),
11799
+ /** The commit the answer was read at, or `worktree` for the working copy. */
11800
+ at: z.string().max(80),
11801
+ /** Milliseconds since the epoch, on the bridge's clock: when it was read. */
11802
+ read_at: z.number().int().min(0),
11803
+ data: FilesDataSchema
11804
+ }), z.strictObject({
11805
+ v: z.literal(1),
11806
+ id: z.string().regex(ULID_PATTERN),
11807
+ product: z.string().regex(ULID_PATTERN),
11808
+ ok: z.literal(false),
11809
+ code: FilesErrorCodeSchema,
11810
+ /** For the platform codes: the host of the repository, so that the device can name it. */
11811
+ host: ForgeHostSchema.optional(),
11812
+ /** For `TOO_LARGE`: the size of the file and what the bridge shows at most. */
11813
+ size: z.number().int().min(0).optional(),
11814
+ max: z.number().int().min(0).optional()
11815
+ })]);
11816
+ readTolerant(FilesDataSchema);
11817
+ readTolerant(FilesResponseSchema);
11818
+ //#endregion
11819
+ //#region ../../packages/protocol/dist/share.js
8869
11820
  /**
8870
- * Warnings drawn from the journal alone, the same on every machine. Machine-local ones (a history
8871
- * rewritten since this machine's last projection, sensitive content this machine cannot read)
8872
- * are never written to `state.json`.
11821
+ * The read-only progress page: a page a creator shares without any account. The device builds it
11822
+ * from a short allow-list (never a copy of the state), seals it with a key made for this one link,
11823
+ * and hands the relay the sealed bytes only. The key travels in the fragment of the link, which no
11824
+ * browser sends to a server, so the relay can neither read the page nor give it to anyone who does
11825
+ * not hold the whole link. The relay erases the page at its expiry or when the creator withdraws
11826
+ * the link.
8873
11827
  */
8874
- const ProjectionWarningSchema = z.discriminatedUnion("code", [
8875
- ChainForkWarningSchema,
8876
- CheckpointIdCollisionWarningSchema,
8877
- CheckpointWeakPayloadWarningSchema,
8878
- LocaleOnHighRiskWarningSchema
8879
- ]);
8880
- const ProjectedDeviceSchema = z.strictObject({
8881
- device: z.string(),
8882
- public_key: z.string()
11828
+ /**
11829
+ * The stages and the phases a page can name. Written here, not imported, so that the page that
11830
+ * reads a link stays light (a test compares them with the lists of the product and of the method).
11831
+ */
11832
+ const SHARE_STAGES = [
11833
+ "framing",
11834
+ "development",
11835
+ "launch",
11836
+ "online"
11837
+ ];
11838
+ const SHARE_PHASES = [
11839
+ "framing",
11840
+ "planning",
11841
+ "design",
11842
+ "build",
11843
+ "delivery",
11844
+ "launch"
11845
+ ];
11846
+ /** The largest sealed page, in bytes. */
11847
+ const SHARE_MAX_SEALED_BYTES = 65536;
11848
+ /** The identifier of a link: 16 random bytes, in base64url (22 characters). */
11849
+ const SHARE_ID_PATTERN = /^[A-Za-z0-9_-]{22}$/;
11850
+ const sealedText = (maxBytes) => z.string().min(1).max(Math.ceil(maxBytes * 4 / 3)).refine(isCanonicalBase64url);
11851
+ z.strictObject({
11852
+ product: z.string().regex(/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/),
11853
+ /** 12 bytes. */
11854
+ nonce: z.string().length(16).refine(isCanonicalBase64url),
11855
+ ciphertext: sealedText(SHARE_MAX_SEALED_BYTES)
8883
11856
  });
11857
+ z.strictObject({ id: z.string().regex(SHARE_ID_PATTERN) });
8884
11858
  z.strictObject({
8885
- format: z.literal(1),
8886
- /** The journal position projected (a commit id in Git), or null for an empty journal. */
8887
- journal_head: z.string().nullable(),
8888
- /** The `at` of the last event applied, in projection order; null when none was applied. */
8889
- computed_at: z.string().nullable(),
8890
- /** The product id of the journal's `product.created`, or null. */
8891
- product: z.string().nullable(),
8892
- /** The registered machines, sorted by `device` (the creators are in `state.humans`). */
8893
- devices: z.array(ProjectedDeviceSchema),
8894
- /** Events applied. */
8895
- applied: z.number().int().min(0),
8896
- /** Events ignored, sorted by `id` then `reason`. */
8897
- ignored: z.array(IgnoredEventSchema),
8898
- warnings: z.array(ProjectionWarningSchema),
8899
- /** What the reducers built. */
8900
- state: z.record(z.string(), z.unknown())
11859
+ v: z.literal(1),
11860
+ /** When the page was made, in milliseconds since the epoch. */
11861
+ made_at: z.number().int().nonnegative(),
11862
+ product: z.strictObject({
11863
+ name: z.string().min(1).max(80),
11864
+ stage: z.enum(SHARE_STAGES)
11865
+ }),
11866
+ phases: z.array(z.strictObject({
11867
+ id: z.enum(SHARE_PHASES),
11868
+ state: z.enum([
11869
+ "done",
11870
+ "current",
11871
+ "review",
11872
+ "upcoming"
11873
+ ])
11874
+ })).max(SHARE_PHASES.length),
11875
+ sprint: z.strictObject({
11876
+ goal: z.string().min(1).max(160).nullable(),
11877
+ done: z.number().int().nonnegative(),
11878
+ total: z.number().int().nonnegative()
11879
+ }).nullable(),
11880
+ /** Stories merged, all sprints together. */
11881
+ delivered: z.number().int().nonnegative(),
11882
+ stories: z.array(z.strictObject({
11883
+ id: z.string().regex(/^[A-Za-z0-9.-]{1,16}$/),
11884
+ /** `null` when the title was held back or the story has none. */
11885
+ title: z.string().min(1).max(120).nullable(),
11886
+ state: z.enum([
11887
+ "todo",
11888
+ "development",
11889
+ "review",
11890
+ "done"
11891
+ ]),
11892
+ /** Steps (acceptance criteria) proven out of those the story lists; `null` when none is listed. */
11893
+ steps: z.strictObject({
11894
+ done: z.number().int().nonnegative(),
11895
+ total: z.number().int().nonnegative()
11896
+ }).nullable()
11897
+ })).max(60),
11898
+ /** Stories of the sprint past the limit, counted and not listed. */
11899
+ more: z.number().int().nonnegative()
8901
11900
  });
8902
11901
  /** A page of checkpoints, newest first: filtered by status, type or story, before an id. */
8903
11902
  const StateCheckpointsInputSchema = z.strictObject({
@@ -8985,6 +11984,87 @@ const CursorCliConfigSchema = z.looseObject({ permissions: z.looseObject({
8985
11984
  allow: z.array(z.string()).optional(),
8986
11985
  deny: z.array(z.string()).optional()
8987
11986
  }).optional() });
11987
+ /** The CSV export's columns: those of `HistoryEntry`, then the reason of an ignored answer. */
11988
+ const HISTORY_CSV_COLUMNS = [...[
11989
+ "at",
11990
+ "author",
11991
+ "author_trust",
11992
+ "author_endorsed_at",
11993
+ "source",
11994
+ "decision",
11995
+ "option",
11996
+ "waiver_until",
11997
+ "assurance",
11998
+ "locale_on_high_risk",
11999
+ "checkpoint",
12000
+ "checkpoint_type",
12001
+ "checkpoint_risk",
12002
+ "checkpoint_title",
12003
+ "story",
12004
+ "event",
12005
+ "checkpoint_phase",
12006
+ "quote",
12007
+ "relayed_by",
12008
+ "channel",
12009
+ "stack",
12010
+ "via"
12011
+ ], "ignored_reason"];
12012
+ /** The UTF-8 byte order mark: spreadsheets then read the accents right. */
12013
+ const BOM = String.fromCharCode(65279);
12014
+ /** Starts a spreadsheet formula when a cell begins with it. */
12015
+ const FORMULA_START = /^[=+\-@\t\r]/;
12016
+ /**
12017
+ * One CSV field (RFC 4180): quoted when it holds a comma, a quote or a line break. A text a
12018
+ * spreadsheet would run as a formula (`=`, `+`, `-`, `@`, tab, CR first) is prefixed with `'`.
12019
+ */
12020
+ function csvField(value) {
12021
+ let text = value === null ? "" : String(value);
12022
+ if (FORMULA_START.test(text)) text = `'${text}`;
12023
+ return /[",\r\n]/.test(text) ? `"${text.replaceAll("\"", "\"\"")}"` : text;
12024
+ }
12025
+ function ignoredRow(item) {
12026
+ return {
12027
+ at: item.at,
12028
+ author: item.author,
12029
+ author_trust: null,
12030
+ author_endorsed_at: null,
12031
+ source: null,
12032
+ decision: item.decision,
12033
+ option: null,
12034
+ waiver_until: null,
12035
+ assurance: null,
12036
+ locale_on_high_risk: null,
12037
+ checkpoint: item.checkpoint,
12038
+ checkpoint_type: null,
12039
+ checkpoint_risk: null,
12040
+ checkpoint_title: null,
12041
+ story: item.story,
12042
+ checkpoint_phase: null,
12043
+ quote: null,
12044
+ relayed_by: null,
12045
+ channel: null,
12046
+ stack: null,
12047
+ via: null,
12048
+ event: item.event,
12049
+ ignored_reason: item.reason
12050
+ };
12051
+ }
12052
+ /**
12053
+ * The CSV export, for spreadsheets (RFC 4180, UTF-8 with a byte order mark so that accents read
12054
+ * right): a header of the English field names, then one record per entry, then (`--all`) one per
12055
+ * ignored answer with its `ignored_reason` (empty for the valid ones). Each line ends with CRLF; a
12056
+ * null is an empty field; formulas are neutralised (`csvField`), so the JSON export is the exact
12057
+ * one.
12058
+ */
12059
+ function historyCsv(entries, ignored = null) {
12060
+ const rows = [...entries.map((entry) => ({
12061
+ ...entry,
12062
+ ignored_reason: null
12063
+ })), ...(ignored ?? []).map(ignoredRow)];
12064
+ const lines = [HISTORY_CSV_COLUMNS.join(",")];
12065
+ for (const row of rows) lines.push(HISTORY_CSV_COLUMNS.map((column) => csvField(row[column])).join(","));
12066
+ return `${BOM}${lines.map((line) => `${line}\r\n`).join("")}`;
12067
+ }
8988
12068
  //#endregion
8989
12069
  //#region ../../packages/crypto/dist/encoding.js
8990
12070
  const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
@@ -9790,9 +12870,4 @@ function gitBlobId(bytes, algorithm = "sha1") {
9790
12870
  return toHex(algorithm === "sha1" ? sha1(object) : sha256$1(object));
9791
12871
  }
9792
12872
  //#endregion
9793
- //#region ../../packages/crypto/dist/webauthn.js
9794
- /** Relying party of every `presence` credential. */
9795
- const WEBAUTHN_RP_ID = "agentplugged.local";
9796
- const WEBAUTHN_ORIGIN = `https://${WEBAUTHN_RP_ID}`;
9797
- //#endregion
9798
- export { MAX_SPRINT_CHECKPOINT_SUMMARY as $, InstallSettingsSchema as $a, RequestCreatorActionInputSchema as $i, STORY_DOCUMENT_ID_PATTERN as $n, REVIEW_LENSES as $r, KNOWLEDGE_CODE_FIELD as $t, REQUIREMENT_STATUSES as A, DeferredIdInputSchema as Aa, hasHiddenCharacter as Ai, SOLO_METHODS as An, resolveGitWorkflow as Ao, AgentOverrideSchema as Ar, HYPOTHESIS_CONFIDENCES as At, RoomIdSchema as B, ReportProgressInputSchema as Ba, ReadMemoryInputSchema as Bi, courseIdOf as Bn, ErrorCode as Bo, qaProofKind as Br, journalRemoteRef as Bt, USAGE_EVENT_TYPES as C, contentSize as Ca, CheckActionInputSchema as Ci, DelegateInputSchema as Cn, issueName as Co, QUESTION_TOOL_VIA as Cr, MigrationStepIdSchema as Ct, DeviationIdInputSchema as D, CompleteDeferredInputSchema as Da, PUBLICATION_DOCS_DIR as Di, CritiqueInputSchema as Dn, GitBranchSchema as Do, BUDGET_EVENT_TYPES as Dr, MERGE_ORDER_EVENT_TYPES as Dt, DEVIATION_EVENT_TYPES as E, plainLine as Ea, SESSION_EVENT_TYPES as Ei, CRITIQUE_EVENT_TYPES as En, GIT_MERGE_MODES as Eo, QuestionToolViaSchema as Er, GetMergeOrderInputSchema as Et, deviationIdOf as F, hasControlCharacter as Fa, MEMORY_EVENT_TYPES as Fi, isCritiqueGesture as Fn, ULID_PATTERN as Fo, MAX_QA_PROOF_BYTES as Fr, JOURNAL_DIR as Ft, THREE_VIEWS_AGENTS as G, AgentIdSchema as Ga, retroIdOf as Gi, DOCUMENT_ID_PATTERN as Gn, REQUIREMENT_EVENT_TYPES as Gr, PresenceCredentialSchema as Gt, RoundTableInputSchema as H, StoryIdSchema as Ha, actionIdOf as Hi, ArchitectureHeaderSchema as Hn, RecordCriterionInputSchema as Hr, HUMAN_DEVICE_PATTERN as Ht, ReviewTestsInputSchema as I, AGENT_EVENT_TYPES as Ia, PROMOTION_TITLE as Ii, isHypothesisOutcome as In, eventInvalid as Io, QA_COMMAND as Ir, JOURNAL_REF as It, AnswerStackInputSchema as J, DEFAULT_VALIDATION_MODE as Ja, CREATOR_ACTION_EVENT_TYPES as Ji, EPIC_DOCUMENT_ID_PATTERN as Jn, SubmitAuditInputSchema as Jr, FAST_TRACK_OPTIONS as Jt, flatTable as K, DEFAULT_INSTALL_MODE as Ka, retroTitle as Ki, DOCUMENT_KINDS as Kn, RecordProofInputSchema as Kr, FAST_TRACK_ENGINE_REASONS as Kt, TEST_CRITERIA as L, MAIN_SESSION_AGENT as La, PromoteLessonInputSchema as Li, COURSE_EVENT_TYPES as Ln, jsonValueIssue as Lo, QA_EVENT_TYPES as Lr, MACHINE_DEVICE_PATTERN as Lt, ResolveDeviationInputSchema as M, canonicalStoryId as Ma, CommonLessonIdSchema as Mi, critiquePathOf as Mn, EVENT_REASON as Mo, AgentFrontmatterSchema as Mr, LOOP_EVENT_TYPES as Mt, TRACED_REQUIREMENT_PATTERN as N, compareStoryIds as Na, LESSON_EVENT_TYPES as Ni, critiqueStepOf as Nn, EVENT_TYPE_PATTERN as No, SkillFrontmatterSchema as Nr, LoopInputSchema as Nt, DeviationReasonSchema as O, DEFERRED_EVENT_TYPES as Oa, PUBLICATION_EXPORT_DIR as Oi, IDEA_VERSION as On, GitWorkflowSchema as Oo, BUDGET_TITLE_PREFIX as Or, QualifyMergeConflictInputSchema as Ot, TracedRequirementInputSchema as P, deferredIdOf as Pa, MEMORY_APPROVING_DECISIONS as Pi, hypothesisIdOf as Pn, SIGNATURE_REASON as Po, AcceptanceQaInputSchema as Pr, EVENT_TYPES as Pt, stackIdOf as Q, INSTALL_MODES as Qa, FORGE_CREATOR_ACTIONS as Qi, REQUIREMENT_ID_PATTERN as Qn, REVIEW_EVENT_TYPES as Qr, DEFAULT_MERGE_METHOD as Qt, TEST_REVIEW_EVENT_TYPES as R, NextInputSchema as Ra, ProposePromotionInputSchema as Ri, COURSE_KINDS as Rn, parseEventEnvelope as Ro, QA_PROOF_DIR as Rr, RESERVED_EVENT_TYPES as Rt, MeasureUsageInputSchema as S, contentSections as Sa, checkpointIdOf as Si, DELEGATION_EVENT_TYPES as Sn, ISSUE as So, QUESTION_TOOL_NAME as Sr, METHOD_EVENT_TYPES as St, UsageMeasuredPayloadSchema as T, isSkillOverrideContent as Ta, RequestActionInputSchema as Ti, CRITIQUE_DOCUMENT_KINDS as Tn, GIT_EVENT_TYPES as To, QuestionStackProofSchema as Tr, CONFLICT_OPTIONS as Tt, TABLE_ANGLES as U, SubmitResultInputSchema as Ua, commonIdOf as Ui, CREATOR_OWNER as Un, criteriaInLine as Ur, HUMAN_EVENT_TYPES as Ut, RoomSchema as V, ReportStatusInputSchema as Va, RecordLessonsInputSchema as Vi, parseCourseStart as Vn, CRITERION_EVENT_TYPES as Vr, journalSeenRef as Vt, TABLE_EVENT_TYPES as W, UpdateStoryInputSchema as Wa, lessonIdOf as Wi, DOCUMENT_DIRS as Wn, criterionId as Wr, HUMAN_ONLY_EVENT_TYPES as Wt, OpenStackInputSchema as X, DEVELOPER_SPECIALTIES as Xa, CreatorActionSchema as Xi, KNOWLEDGE_SOURCES_FIELD as Xn, JUDGE_LENS as Xr, StartStoryFastTrackInputSchema as Xt, AskStackInputSchema as Y, DEFAULT_VALIDATION_POINTS as Ya, CREATOR_ACTION_KINDS as Yi, EpicHeaderSchema as Yn, SyncRequirementsInputSchema as Yr, LeaveFastTrackInputSchema as Yt, STACK_EVENT_TYPES as Z, DOMAIN_IDS as Za, FILE_CREATOR_ACTIONS as Zi, KnowledgeSourceSchema as Zn, REVIEW_DOMAINS as Zr, missingDeclarations as Zt, currentEvent as _, AgentContentSchema as _a, NOTE_REQUIRED as _i, devicePairedRecord as _n, INHERIT_MODEL as _o, AnswerCheckpointInputSchema as _r, ReadSensitiveInputSchema as _t, sha256 as a, REVIEW_SKILLS as aa, findingIdOf as ai, RepositoryPathSchema as an, PullRequestSettingsSchema as ao, INSTALL_EVENT_TYPES as ar, READINESS_EVENT_TYPES as at, isSensitiveEventType as b, SKILL_OVERRIDE_FIELDS as ba, RESOLUTION_DECISIONS as bi, pairOfferRecord as bn, ModelTableSchema as bo, CHAT_RETENTION_MS as br, isSensitivePath as bt, fromUtf8 as c, RunProofCommandInputSchema as ca, reviewIdOf as ci, ELICITATION_TIMEOUT_MS as cn, RulesSchema as co, CheckCiInputSchema as cr, ReadyInputSchema as ct, utf8 as d, isStoryStep as da, STORY_EVENT_TYPES as di, BRIDGE_ID_PATTERN as dn, TEST_FILE_MAX_LINES as do, CiWaiverReasonSchema as dr, ENCRYPTED_FILE_PREFIX as dt, engineUpdateRisk as ea, SECURITY_REASONS as ei, KNOWLEDGE_STORY_FIELD as en, MethodConfigSchema as eo, STORY_SECTIONS as er, MAX_SPRINT_USD as et, ClaudeSettingsSchema as f, ARCHITECTURE_PREREQUISITES_FIELD as fa, isStoryBranch as fi, DEVICE_TRUST_EVENT_TYPES as fn, TOOL_IDS as fo, MAX_CI_CHECKS as fr, KEY_ID_PATTERN as ft, JOURNAL_PROJECTED_REF as g, PrerequisitesSchema as ga, LIFTING_DECISIONS as gi, deviceKindOf as gn, ValidationSettingsSchema as go, ANTICIPATED_ANSWER_WINDOW_MS as gr, PRODUCT_KEY_EXPORT_PREFIX as gt, GetStateInputSchema as h, PrerequisiteVersionSchema as ha, CheckpointIdSchema as hi, TRUST_SIGNATURE_TAGS as hn, VALIDATION_POINTS as ho, CHECKPOINT_CLOSED_EVENT_TYPES as hr, PRODUCT_KEY_EVENT_TYPES as ht, randomBytes as i, WRITE_TEXT_MAX as ia, TriageReviewInputSchema as ii, MERGE_EVENT_TYPES as in, ProfileSchema as io, DependenciesSchema as ir, sprintIdOf as it, ReportDeviationInputSchema as j, DeferredReasonSchema as ja, hasMisleadingControl as ji, critiqueIdOf as jn, sameWorkflow as jo, menuKey as jr, HYPOTHESIS_KIND as jt, GetTraceInputSchema as k, DeferWorkInputSchema as ka, countWords as ki, RECONCILE_KINDS as kn, recommendedGitWorkflow as ko, AgentDefinitionSchema as kr, SUBSTANTIVE_CLASSES as kt, toBase64url as l, normalizedFolder as la, CommitIdSchema as li, ElicitationChannelSchema as ln, SemverSchema as lo, CiReportedPayloadSchema as lr, readinessIdOf as lt, SyncManifestSchema as m, PrerequisiteCommandSchema as ma, CONFIRMATION_MODES as mi, RootPinSchema as mn, VALIDATION_MODES as mo, DecideCheckpointInputSchema as mr, PAYLOAD_ENCRYPTION_ALG as mt, WEBAUTHN_RP_ID as n, CheckWriteInputSchema as na, StoryReviewInputSchema as ni, KnowledgeStorySchema as nn, PHASE_IDS as no, epicIdOf as nr, SPRINT_EVENT_TYPES as nt, sha256Hex as o, SkillOverrideSchema as oa, isSilentJudge as oi, AskCreatorInputSchema as on, RULE_ACTIONS as oo, CI_EVENT_TYPES as or, READINESS_FIX_FIRST as ot, CursorCliConfigSchema as p, EnvironmentCheckIdSchema as pa, CHECKPOINT_EVENT_TYPES as pi, DEVICE_TRUST_SYSTEM_EVENT_TYPES as pn, TestSettingsSchema as po, CHECKPOINT_DECIDED_EVENT_TYPES as pr, MAX_SENSITIVE_CONTENT_BYTES as pt, roundTableStepOf as q, DEFAULT_PERMISSION_MODE as qa, scopeLabel as qi, DocumentHeaderSchema as qn, SettleGapInputSchema as qr, FAST_TRACK_EVENT_TYPES as qt, gitBlobId as r, WRITE_GUARD_EVENT_TYPES as ra, SubmitReviewInputSchema as ri, MAX_MERGED_FILES as rn, PROFILE_PRESET_IDS as ro, ARCHITECTURE_DEPENDENCIES_FIELD as rr, SPRINT_TITLE_PREFIX as rt, fromBase64url as s, ProofsFileSchema as sa, isSilentLens as si, ELICITATION_EVENT_TYPES as sn, RULE_VALUES as so, CI_FORGES as sr, READINESS_GO_ON as st, WEBAUTHN_ORIGIN as t, proofCommandsRisk as ta, StartReviewInputSchema as ti, KnowledgeCodeSchema as tn, PERMISSION_MODES as to, StoryHeaderSchema as tr, PlanSprintInputSchema as tt, toHex as u, PHASE_STEPS as ua, OpenPullRequestInputSchema as ui, MAX_ELICITED_NOTE_LENGTH as un, SkillNameSchema as uo, CiUrlSchema as ur, readinessStepOf as ut, eventTypeSpec as v, FORM_CONTENT_MAX_CHARS as va, OpenCheckpointInputSchema as vi, deviceRevokedRecord as vn, MODEL_CLIENT_IDS as vo, CHAT_DECISIONS as vr, SENSITIVE_AREAS as vt, USAGE_SOURCES as w, isSkillContent as wa, RecordExternalReadInputSchema as wi, NOAH_AGENT as wn, GIT_CI_MODES as wo, QuestionProofSchema as wr, CONFLICT_CLASSES as wt, isTestEventType as x, SkillContentSchema as xa, ReportMeasuresInputSchema as xi, rootCertifiedRecord as xn, NO_EFFORT as xo, MessageDigestSchema as xr, utf8Length as xt, eventTypeUpgrade as y, REQUIRED_CONTENT_FIELDS as ya, RECIPIENT_ROLES as yi, isTrustSignatureTag as yn, MODEL_TIERS as yo, CHAT_EVENT_TYPES as yr, WriteSensitiveInputSchema as yt, TEST_ROWS as z, ReportCostInputSchema as za, ProposeRetrospectiveInputSchema as zi, CourseInputSchema as zn, TEST_EVENT_TYPE as zo, qaProofBelongs as zr, journalEventPath as zt };
12873
+ export { readinessIdOf as $, AGENT_EVENT_TYPES as $a, PROMOTION_TITLE as $i, epicIdOf as $n, EVENT_REASON as $o, QA_COMMAND as $r, MAX_MERGED_FILES as $t, RoomIdSchema as A, EnvironmentCheckIdSchema as Aa, CHECKPOINT_EVENT_TYPES as Ai, critiqueStepOf as An, SkillNameSchema as Ao, deviceRevokedRecord as Ar, EVENT_TYPES as At, OpenStackInputSchema as B, contentSize as Ba, CheckActionInputSchema as Bi, CREATOR_OWNER as Bn, MODEL_TIERS as Bo, QUESTION_TOOL_NAME as Br, HUMAN_ONLY_EVENT_TYPES as Bt, USAGE_EVENT_TYPES as C, SkillOverrideSchema as Ca, isSilentJudge as Ci, CRITIQUE_EVENT_TYPES as Cn, PROFILE_PRESET_SPECS as Co, BRIDGE_ID_PATTERN as Cr, QualifyMergeConflictInputSchema as Ct, TEST_CRITERIA as D, PHASE_STEPS as Da, OpenPullRequestInputSchema as Di, SOLO_METHODS as Dn, RULE_VALUES as Do, TRUST_SIGNATURE_TAGS as Dr, HYPOTHESIS_KIND as Dt, ReviewTestsInputSchema as E, normalizedFolder as Ea, CommitIdSchema as Ei, RECONCILE_KINDS as En, RULE_ACTIONS as Eo, RootPinSchema as Er, HYPOTHESIS_CONFIDENCES as Et, THREE_VIEWS_AGENTS as F, FORM_CONTENT_MAX_CHARS as Fa, OpenCheckpointInputSchema as Fi, COURSE_KINDS as Fn, VALIDATION_POINTS as Fo, AnswerCheckpointInputSchema as Fr, journalEventPath as Ft, PlanSprintInputSchema as G, DEFERRED_EVENT_TYPES as Ga, PUBLICATION_EXPORT_DIR as Gi, EPIC_DOCUMENT_ID_PATTERN as Gn, GIT_CI_MODES as Go, RULE_LOCKS as Gr, LeaveFastTrackInputSchema as Gt, stackIdOf as H, isSkillOverrideContent as Ha, RequestActionInputSchema as Hi, DOCUMENT_ID_PATTERN as Hn, NO_EFFORT as Ho, QuestionProofSchema as Hr, FAST_TRACK_ENGINE_REASONS as Ht, flatTable as I, REQUIRED_CONTENT_FIELDS as Ia, RECIPIENT_ROLES as Ii, CourseInputSchema as In, ValidationSettingsSchema as Io, CHAT_DECISIONS as Ir, journalRemoteRef as It, sprintIdOf as J, DeferredReasonSchema as Ja, hasMisleadingControl as Ji, KnowledgeSourceSchema as Jn, GitBranchSchema as Jo, menuKey as Jr, DEFAULT_MERGE_METHOD as Jt, SPRINT_EVENT_TYPES as K, DeferWorkInputSchema as Ka, countWords as Ki, EpicHeaderSchema as Kn, GIT_EVENT_TYPES as Ko, AgentDefinitionSchema as Kr, StartStoryFastTrackInputSchema as Kt, roundTableStepOf as L, SKILL_OVERRIDE_FIELDS as La, RESOLUTION_DECISIONS as Li, courseIdOf as Ln, INHERIT_MODEL as Lo, CHAT_EVENT_TYPES as Lr, journalSeenRef as Lt, RoundTableInputSchema as M, PrerequisiteVersionSchema as Ma, CheckpointIdSchema as Mi, isCritiqueGesture as Mn, TOOL_IDS as Mo, pairOfferRecord as Mr, JOURNAL_REF as Mt, TABLE_ANGLES as N, PrerequisitesSchema as Na, LIFTING_DECISIONS as Ni, isHypothesisOutcome as Nn, TestSettingsSchema as No, rootCertifiedRecord as Nr, MACHINE_DEVICE_PATTERN as Nt, TEST_REVIEW_EVENT_TYPES as O, isStoryStep as Oa, STORY_EVENT_TYPES as Oi, critiqueIdOf as On, RulesSchema as Oo, deviceKindOf as Or, LOOP_EVENT_TYPES as Ot, TABLE_EVENT_TYPES as P, AgentContentSchema as Pa, NOTE_REQUIRED as Pi, COURSE_EVENT_TYPES as Pn, VALIDATION_MODES as Po, ANTICIPATED_ANSWER_WINDOW_MS as Pr, RESERVED_EVENT_TYPES as Pt, ReadyInputSchema as Q, hasControlCharacter as Qa, MEMORY_EVENT_TYPES as Qi, StoryHeaderSchema as Qn, sameWorkflow as Qo, MAX_QA_PROOF_BYTES as Qr, KnowledgeStorySchema as Qt, AnswerStackInputSchema as R, SkillContentSchema as Ra, ReportMeasuresInputSchema as Ri, parseCourseStart as Rn, LEGACY_GEMINI_CLIENT as Ro, CHAT_RETENTION_MS as Rr, HUMAN_DEVICE_PATTERN as Rt, MeasureUsageInputSchema as S, REVIEW_SKILLS as Sa, findingIdOf as Si, CRITIQUE_DOCUMENT_KINDS as Sn, PROFILE_PRESET_IDS as So, WEB_ANSWER_WINDOW_MS as Sr, MERGE_ORDER_EVENT_TYPES as St, UsageMeasuredPayloadSchema as T, RunProofCommandInputSchema as Ta, reviewIdOf as Ti, IDEA_VERSION as Tn, PullRequestSettingsSchema as To, DEVICE_TRUST_SYSTEM_EVENT_TYPES as Tr, MACHINE_EVENT_TYPES as Tt, MAX_SPRINT_CHECKPOINT_SUMMARY as U, plainLine as Ua, SESSION_EVENT_TYPES as Ui, DOCUMENT_KINDS as Un, ISSUE as Uo, QuestionStackProofSchema as Ur, FAST_TRACK_EVENT_TYPES as Ut, STACK_EVENT_TYPES as V, isSkillContent as Va, RecordExternalReadInputSchema as Vi, DOCUMENT_DIRS as Vn, ModelTableSchema as Vo, QUESTION_TOOL_VIA as Vr, PresenceCredentialSchema as Vt, MAX_SPRINT_USD as W, CompleteDeferredInputSchema as Wa, PUBLICATION_DOCS_DIR as Wi, DocumentHeaderSchema as Wn, issueName as Wo, QuestionToolViaSchema as Wr, FAST_TRACK_OPTIONS as Wt, READINESS_FIX_FIRST as X, compareStoryIds as Xa, LESSON_EVENT_TYPES as Xi, STORY_DOCUMENT_ID_PATTERN as Xn, recommendedGitWorkflow as Xo, SkillFrontmatterSchema as Xr, KNOWLEDGE_STORY_FIELD as Xt, READINESS_EVENT_TYPES as Y, canonicalStoryId as Ya, CommonLessonIdSchema as Yi, REQUIREMENT_ID_PATTERN as Yn, GitWorkflowSchema as Yo, AgentFrontmatterSchema as Yr, KNOWLEDGE_CODE_FIELD as Yt, READINESS_GO_ON as Z, deferredIdOf as Za, MEMORY_APPROVING_DECISIONS as Zi, STORY_SECTIONS as Zn, resolveGitWorkflow as Zo, AcceptanceQaInputSchema as Zr, KnowledgeCodeSchema as Zt, currentEvent as _, engineUpdateRisk as _a, SECURITY_REASONS as _i, TracedRequirementInputSchema as _n, INSTALL_MODES as _o, BUDGET_RECOMMENDATION as _r, METHOD_EVENT_TYPES as _t, fromBase64url as a, actionIdOf as aa, RecordCriterionInputSchema as ai, ELICITATION_EVENT_TYPES as an, StoryIdSchema as ao, CheckCiInputSchema as ar, parseEventEnvelope as as, PRODUCT_KEYS_EXPORT_PREFIX as at, isSensitiveEventType as b, WRITE_GUARD_EVENT_TYPES as ba, SubmitReviewInputSchema as bi, DelegateInputSchema as bn, PERMISSION_MODES as bo, WEB_ANSWER_EVENT_TYPES as br, CONFLICT_OPTIONS as bt, toHex as c, retroIdOf as ca, REQUIREMENT_EVENT_TYPES as ci, MAX_ELICITED_NOTE_LENGTH as cn, AgentIdSchema as co, CiWaiverReasonSchema as cr, TEST_EVENT_TYPE as cs, ProductKeyRotatedPayloadSchema as ct, ClaudeSettingsSchema as d, CREATOR_ACTION_EVENT_TYPES as da, SubmitAuditInputSchema as di, DeviationReasonSchema as dn, DEFAULT_ON_EXCEED as do, DecideCheckpointInputSchema as dr, WriteSensitiveInputSchema as dt, PromoteLessonInputSchema as ea, QA_EVENT_TYPES as ei, MERGE_EVENT_TYPES as en, MAIN_SESSION_AGENT as eo, ARCHITECTURE_DEPENDENCIES_FIELD as er, EVENT_TYPE_PATTERN as es, readinessStepOf as et, CursorCliConfigSchema as f, CREATOR_ACTION_KINDS as fa, SyncRequirementsInputSchema as fi, GetTraceInputSchema as fn, DEFAULT_PERMISSION_MODE as fo, CHECKPOINT_CLOSED_EVENT_TYPES as fr, isSensitivePath as ft, JOURNAL_PROJECTED_REF as g, RequestCreatorActionInputSchema as ga, REVIEW_LENSES as gi, TRACED_REQUIREMENT_PATTERN as gn, DOMAIN_IDS as go, BUDGET_OPTIONS as gr, TEAM_EVENT_TYPES as gt, REDACTED_SECRET as h, FORGE_CREATOR_ACTIONS as ha, REVIEW_EVENT_TYPES as hi, ResolveDeviationInputSchema as hn, DEVELOPER_SPECIALTIES as ho, BUDGET_EVENT_TYPES as hr, PauseTeamInputSchema as ht, sha256Hex as i, RecordLessonsInputSchema as ia, CRITERION_EVENT_TYPES as ii, AskCreatorInputSchema as in, ReportStatusInputSchema as io, CI_FORGES as ir, jsonValueIssue as is, PAYLOAD_ENCRYPTION_ALG as it, RoomSchema as j, PrerequisiteCommandSchema as ja, CONFIRMATION_MODES as ji, hypothesisIdOf as jn, TEST_FILE_MAX_LINES as jo, isTrustSignatureTag as jr, JOURNAL_DIR as jt, TEST_ROWS as k, ARCHITECTURE_PREREQUISITES_FIELD as ka, isStoryBranch as ki, critiquePathOf as kn, SemverSchema as ko, devicePairedRecord as kr, LoopInputSchema as kt, utf8 as l, retroTitle as la, RecordProofInputSchema as li, DEVIATION_EVENT_TYPES as ln, DEFAULT_ALERT_RATIO as lo, MAX_CI_CHECKS as lr, ErrorCode as ls, ReadSensitiveInputSchema as lt, GetStateInputSchema as m, FILE_CREATOR_ACTIONS as ma, REVIEW_DOMAINS as mi, ReportDeviationInputSchema as mn, DEFAULT_VALIDATION_POINTS as mo, CheckCallInputSchema as mr, ORDER_EVENT_TYPES as mt, randomBytes as n, ProposeRetrospectiveInputSchema as na, qaProofBelongs as ni, EXCHANGE_EVENT_TYPES as nn, ReportCostInputSchema as no, INSTALL_EVENT_TYPES as nr, ULID_PATTERN as ns, KEY_ID_PATTERN as nt, fromUtf8 as o, commonIdOf as oa, criteriaInLine as oi, ELICITATION_TIMEOUT_MS as on, SubmitResultInputSchema as oo, CiReportedPayloadSchema as or, extensionModuleOf as os, PRODUCT_KEY_EVENT_TYPES as ot, SyncManifestSchema as p, CreatorActionSchema as pa, JUDGE_LENS as pi, REQUIREMENT_STATUSES as pn, DEFAULT_VALIDATION_MODE as po, CHAIN_EVENT_TYPES as pr, utf8Length as pt, SPRINT_TITLE_PREFIX as q, DeferredIdInputSchema as qa, hasHiddenCharacter as qi, KNOWLEDGE_SOURCES_FIELD as qn, GIT_MERGE_MODES as qo, AgentOverrideSchema as qr, missingDeclarations as qt, sha256 as r, ReadMemoryInputSchema as ra, qaProofKind as ri, SummarizeInputSchema as rn, ReportProgressInputSchema as ro, CI_EVENT_TYPES as rr, eventInvalid as rs, MAX_SENSITIVE_CONTENT_BYTES as rt, toBase64url as s, lessonIdOf as sa, criterionId as si, ElicitationChannelSchema as sn, UpdateStoryInputSchema as so, CiUrlSchema as sr, isExtensionEventType as ss, PRODUCT_KEY_EXPORT_PREFIX as st, gitBlobId as t, ProposePromotionInputSchema as ta, QA_PROOF_DIR as ti, RepositoryPathSchema as tn, NextInputSchema as to, DependenciesSchema as tr, SIGNATURE_REASON as ts, ENCRYPTED_FILE_PREFIX as tt, historyCsv as u, scopeLabel as ua, SettleGapInputSchema as ui, DeviationIdInputSchema as un, DEFAULT_INSTALL_MODE as uo, CHECKPOINT_DECIDED_EVENT_TYPES as ur, SENSITIVE_AREAS as ut, eventTypeSpec as v, proofCommandsRisk as va, StartReviewInputSchema as vi, deviationIdOf as vn, InstallSettingsSchema as vo, BUDGET_TITLE_PREFIX as vr, MigrationStepIdSchema as vt, USAGE_SOURCES as w, ProofsFileSchema as wa, isSilentLens as wi, CritiqueInputSchema as wn, ProfileSchema as wo, DEVICE_TRUST_EVENT_TYPES as wr, SUBSTANTIVE_CLASSES as wt, isTestEventType as x, WRITE_TEXT_MAX as xa, TriageReviewInputSchema as xi, NOAH_AGENT as xn, PHASE_IDS as xo, WEB_ANSWER_STALE_MS as xr, GetMergeOrderInputSchema as xt, eventTypeUpgrade as y, CheckWriteInputSchema as ya, StoryReviewInputSchema as yi, DELEGATION_EVENT_TYPES as yn, MethodConfigSchema as yo, MAX_BUDGET_USD as yr, CONFLICT_CLASSES as yt, AskStackInputSchema as z, contentSections as za, checkpointIdOf as zi, ArchitectureHeaderSchema as zn, MODEL_CLIENT_IDS as zo, MessageDigestSchema as zr, HUMAN_EVENT_TYPES as zt };