agentplugged 1.0.4 → 1.2.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.
@@ -695,6 +695,16 @@ const EVENT_CONVERSIONS = [
695
695
  from: 13,
696
696
  convert: (payload) => ({ ...payload })
697
697
  },
698
+ {
699
+ type: "checkpoint.opened",
700
+ from: 14,
701
+ convert: (payload) => ({ ...payload })
702
+ },
703
+ {
704
+ type: "checkpoint.opened",
705
+ from: 15,
706
+ convert: (payload) => ({ ...payload })
707
+ },
698
708
  {
699
709
  type: "creator_action.executed",
700
710
  from: 11,
@@ -715,6 +725,26 @@ const EVENT_CONVERSIONS = [
715
725
  from: 2,
716
726
  convert: (payload) => ({ ...payload })
717
727
  },
728
+ {
729
+ type: "story.merged",
730
+ from: 3,
731
+ convert: (payload) => ({ ...payload })
732
+ },
733
+ {
734
+ type: "story.merged",
735
+ from: 4,
736
+ convert: (payload) => ({ ...payload })
737
+ },
738
+ {
739
+ type: "checkpoint.web_answered",
740
+ from: 1,
741
+ convert: (payload) => ({ ...payload })
742
+ },
743
+ {
744
+ type: "checkpoint.web_held",
745
+ from: 1,
746
+ convert: (payload) => ({ ...payload })
747
+ },
718
748
  {
719
749
  type: "checkpoint.closed",
720
750
  from: 1,
@@ -756,19 +786,21 @@ const EVENT_CONVERSIONS = [
756
786
  * raised when a payload gains fields a strict schema of an older engine would refuse: that
757
787
  * engine then asks for an upgrade (`UPGRADE_REQUIRED`) instead of ignoring the event as invalid.
758
788
  * `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,
789
+ * 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
790
  * `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
791
  * `checkpoint.elicited` and `stack.answered` at 2, `usage.measured` and
762
792
  * `agent.status_reported` at 2.
763
793
  */
764
794
  const EVENT_TYPE_VERSIONS = {
765
- "checkpoint.opened": 14,
795
+ "checkpoint.opened": 16,
766
796
  "checkpoint.closed": 2,
767
797
  "checkpoint.answered": 2,
768
798
  "cost.reported": 2,
769
799
  "requirement.proved": 2,
770
800
  "creator_action.executed": 12,
771
- "story.merged": 3,
801
+ "story.merged": 5,
802
+ "checkpoint.web_answered": 2,
803
+ "checkpoint.web_held": 2,
772
804
  "checkpoint.elicited": 2,
773
805
  "stack.answered": 2,
774
806
  "usage.measured": 2,
@@ -1307,16 +1339,19 @@ const MODEL_TIERS = [
1307
1339
  "multimodal"
1308
1340
  ];
1309
1341
  /**
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`.
1342
+ * Clients of the model table: the AI tools as they read a sub-agent.
1312
1343
  */
1313
1344
  const MODEL_CLIENT_IDS = [
1314
1345
  "claude-code",
1315
1346
  "codex",
1316
1347
  "antigravity",
1317
- "gemini-cli",
1318
1348
  "cursor"
1319
1349
  ];
1350
+ /**
1351
+ * Key of the retired Gemini CLI in a model table or in the `models` section of `method.yaml`.
1352
+ * Older products still hold it: it is read, reported and ignored, never accepted for anything new.
1353
+ */
1354
+ const LEGACY_GEMINI_CLIENT = "gemini-cli";
1320
1355
  /** Model value that keeps the model of the session: no model is chosen per agent. */
1321
1356
  const INHERIT_MODEL = "inherit";
1322
1357
  const Value = z.string().trim().min(1);
@@ -1369,7 +1404,7 @@ const ModelTableSchema = z.strictObject({
1369
1404
  "claude-code": tiers(ClaudeCell),
1370
1405
  codex: tiers(CodexCell),
1371
1406
  antigravity: tiers(ModelOnly),
1372
- "gemini-cli": tiers(ModelOnly),
1407
+ [LEGACY_GEMINI_CLIENT]: z.unknown().optional(),
1373
1408
  cursor: tiers(ModelOnly)
1374
1409
  });
1375
1410
  function partialTiers(cell) {
@@ -1394,7 +1429,7 @@ const ModelOverridesSchema = z.strictObject({
1394
1429
  effort: z.enum([...CODEX_EFFORTS, NO_EFFORT]).optional()
1395
1430
  })),
1396
1431
  antigravity: partialTiers(ModelOnly.partial()),
1397
- "gemini-cli": partialTiers(ModelOnly.partial()),
1432
+ [LEGACY_GEMINI_CLIENT]: z.unknown().optional(),
1398
1433
  cursor: partialTiers(ModelOnly.partial())
1399
1434
  });
1400
1435
  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 +1518,48 @@ const ProfileSchema = z.strictObject({
1483
1518
  message: "must be empty while the code domain is inactive"
1484
1519
  });
1485
1520
  });
1521
+ /** The starting profiles. */
1522
+ const PROFILE_PRESET_SPECS = {
1523
+ "new-product": {
1524
+ domains: DOMAIN_IDS,
1525
+ phases: PHASE_IDS,
1526
+ specialties: ["backend", "frontend"]
1527
+ },
1528
+ maintenance: {
1529
+ domains: [
1530
+ "code",
1531
+ "quality",
1532
+ "security",
1533
+ "delivery",
1534
+ "docs"
1535
+ ],
1536
+ phases: [
1537
+ "design",
1538
+ "build",
1539
+ "delivery"
1540
+ ],
1541
+ specialties: ["backend", "frontend"]
1542
+ },
1543
+ "launch-only": {
1544
+ domains: [
1545
+ "product",
1546
+ "creativity",
1547
+ "marketing",
1548
+ "sales"
1549
+ ],
1550
+ phases: ["framing", "launch"],
1551
+ specialties: []
1552
+ },
1553
+ prototype: {
1554
+ domains: [
1555
+ "product",
1556
+ "ux",
1557
+ "code"
1558
+ ],
1559
+ phases: ["framing", "build"],
1560
+ specialties: ["backend", "frontend"]
1561
+ }
1562
+ };
1486
1563
  function idList() {
1487
1564
  return z.array(SkillNameSchema).refine((list) => new Set(list).size === list.length, { message: ISSUE.duplicateValue });
1488
1565
  }
@@ -3590,7 +3667,7 @@ function checkpointIdOf(eventId) {
3590
3667
  /** A free text of 1 to `max` characters. */
3591
3668
  const text = (max) => z.string().trim().min(1).max(max);
3592
3669
  /** 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" });
3670
+ 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
3671
  /** A measure's name: `tests_failed`, `coverage`, `lighthouse.performance`. */
3595
3672
  const MetricSchema = z.string().regex(/^[a-z][a-z0-9_.]{0,63}$/, { message: "expected a lowercase name of letters, digits, dots and underscores" });
3596
3673
  /**
@@ -3619,13 +3696,13 @@ const CheckpointBlockSchema = z.strictObject({
3619
3696
  });
3620
3697
  /** One answer of a decision: what it is, and what it entails. */
3621
3698
  const CheckpointOptionSchema = z.strictObject({
3622
- id: ShortIdSchema$3,
3699
+ id: ShortIdSchema$4,
3623
3700
  label: text(200),
3624
3701
  consequence: text(1e3)
3625
3702
  });
3626
3703
  /** A rule on a measure: `<metric> <op> <threshold>`. */
3627
3704
  const CheckpointCriterionSchema = z.strictObject({
3628
- id: ShortIdSchema$3,
3705
+ id: ShortIdSchema$4,
3629
3706
  metric: MetricSchema,
3630
3707
  op: z.enum(CRITERION_OPERATORS),
3631
3708
  threshold: z.number().finite(),
@@ -3633,16 +3710,38 @@ const CheckpointCriterionSchema = z.strictObject({
3633
3710
  });
3634
3711
  /** Measures reported for a checkpoint: `{ <metric>: number }`, 1 to 50 of them. */
3635
3712
  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" });
3713
+ /**
3714
+ * What the creator reads first on a checkpoint, in plain words: what changes for the people who
3715
+ * use the product (`effect`), what could go wrong (`risks`) and what the agents consulted think
3716
+ * (`opinions`, one sentence each). Optional: a checkpoint without a card is read from its
3717
+ * `summary`. The fuller text stays in `summary`, one click away. `why` answers "why this?" in
3718
+ * place: one sentence for the reason, one for the alternative set aside.
3719
+ */
3720
+ const CheckpointCardSchema = z.strictObject({
3721
+ effect: text(300),
3722
+ why: z.strictObject({
3723
+ reason: text(200),
3724
+ /** Absent when nothing else was weighed. */
3725
+ rejected: text(200).optional()
3726
+ }).optional(),
3727
+ risks: z.array(text(200)).min(1).max(3).optional(),
3728
+ opinions: z.array(z.strictObject({
3729
+ agent: AgentIdSchema.max(64),
3730
+ view: text(200)
3731
+ })).min(1).max(4).optional()
3732
+ });
3636
3733
  /** The fields an agent gives, whose defaults `core` fixes (recipient, confirmation). */
3637
3734
  const describedFields = {
3638
3735
  type: z.enum(CHECKPOINT_TYPES),
3639
3736
  risk: z.enum(CHECKPOINT_RISKS),
3640
3737
  title: text(120),
3641
3738
  summary: text(2e3),
3739
+ /** The first lines the creator reads: the effect for the users, the risks, the agents' views. */
3740
+ card: CheckpointCardSchema.optional(),
3642
3741
  documents: z.array(CheckpointDocumentSchema).max(20),
3643
3742
  blocks: z.array(CheckpointBlockSchema).max(10),
3644
3743
  options: z.array(CheckpointOptionSchema).min(2).max(4).optional(),
3645
- recommendation: ShortIdSchema$3.optional(),
3744
+ recommendation: ShortIdSchema$4.optional(),
3646
3745
  criteria: z.array(CheckpointCriterionSchema).min(1).max(20).optional(),
3647
3746
  /** The phase a `phase_end` checkpoint closes: given with that type, and only with it. */
3648
3747
  phase: z.enum(PHASE_IDS).optional()
@@ -3903,7 +4002,7 @@ const NOTE_REQUIRED = /* @__PURE__ */ new Set([
3903
4002
  const CheckpointResolvedPayloadSchema = z.strictObject({
3904
4003
  checkpoint: CheckpointIdSchema,
3905
4004
  decision: z.enum(RESOLUTION_DECISIONS),
3906
- option: ShortIdSchema$3.optional(),
4005
+ option: ShortIdSchema$4.optional(),
3907
4006
  note: text(2e3).optional(),
3908
4007
  waiver_until: z.iso.datetime().optional(),
3909
4008
  source: z.enum(RESOLUTION_SOURCES)
@@ -4807,6 +4906,117 @@ const QaReportedPayloadSchema = z.strictObject({
4807
4906
  path: ["results"]
4808
4907
  });
4809
4908
  //#endregion
4909
+ //#region ../../packages/protocol/dist/account.js
4910
+ /**
4911
+ * The wire contract of the account routes of the relay: creating an account (the address is proved
4912
+ * by a code sent by e-mail, then a passkey is made), signing in with a passkey, reading and
4913
+ * changing the profile, signing out. No password exists anywhere in it: a passkey is the only way
4914
+ * in. The address is unique: one person, one account.
4915
+ */
4916
+ /** The languages an account can choose. */
4917
+ const ACCOUNT_LOCALES = [
4918
+ "fr",
4919
+ "en",
4920
+ "es",
4921
+ "pt"
4922
+ ];
4923
+ /** An address as the relay keeps it: trimmed and lowercased. */
4924
+ const AccountEmailSchema = z.string().transform((value) => value.trim().toLowerCase()).pipe(z.string().max(254).regex(/^[^\s@]+@[^\s@]+\.[^\s@]+$/));
4925
+ const AccountLocaleSchema = z.enum(ACCOUNT_LOCALES);
4926
+ z.strictObject({
4927
+ email: AccountEmailSchema,
4928
+ locale: AccountLocaleSchema
4929
+ });
4930
+ z.strictObject({
4931
+ verification: z.string().min(1).max(64),
4932
+ /** How long the code works, in minutes. */
4933
+ valid_minutes: z.number().int().positive()
4934
+ });
4935
+ z.strictObject({
4936
+ verification: z.string().min(1).max(64),
4937
+ code: z.string().regex(new RegExp(`^[0-9]{6}$`))
4938
+ });
4939
+ z.strictObject({ verification: z.string().min(1).max(64) });
4940
+ z.strictObject({});
4941
+ z.object({
4942
+ ceremony: z.string().min(1).max(64),
4943
+ options: z.record(z.string(), z.unknown())
4944
+ });
4945
+ z.strictObject({
4946
+ ceremony: z.string().min(1).max(64),
4947
+ response: z.record(z.string(), z.unknown())
4948
+ });
4949
+ z.strictObject({ ok: z.literal(true) });
4950
+ z.strictObject({
4951
+ ok: z.literal(true),
4952
+ session: z.string().min(1).max(128),
4953
+ expires_at: z.string()
4954
+ });
4955
+ z.strictObject({
4956
+ account_id: z.string().regex(ULID_PATTERN),
4957
+ email: z.string().nullable(),
4958
+ locale: AccountLocaleSchema.nullable(),
4959
+ created_at: z.string()
4960
+ });
4961
+ z.strictObject({ locale: AccountLocaleSchema });
4962
+ /** An export of the account's data, as the relay describes it to its owner. */
4963
+ const AccountExportStatusSchema = z.strictObject({
4964
+ id: z.string().regex(ULID_PATTERN),
4965
+ status: z.enum(["pending", "ready"]),
4966
+ requested_at: z.string(),
4967
+ /** The date the export is promised by (it is usually ready within seconds). */
4968
+ due_at: z.string(),
4969
+ ready_at: z.string().nullable(),
4970
+ /** The date the link stops working, once the export is ready. */
4971
+ expires_at: z.string().nullable()
4972
+ });
4973
+ z.strictObject({ export: AccountExportStatusSchema.nullable() });
4974
+ z.strictObject({ id: z.string().regex(ULID_PATTERN) });
4975
+ z.strictObject({ confirm: z.literal(true) });
4976
+ /** One passkey of the account, as its owner sees it: public facts only. */
4977
+ const AccountPasskeySchema = z.strictObject({
4978
+ /** The credential identifier, base64url (public: it is not a secret). */
4979
+ id: z.string().min(1).max(1024),
4980
+ created_at: z.string(),
4981
+ last_used_at: z.string().nullable()
4982
+ });
4983
+ z.strictObject({ passkeys: z.array(AccountPasskeySchema).max(10) });
4984
+ z.strictObject({ id: z.string().min(1).max(1024) });
4985
+ //#endregion
4986
+ //#region ../../packages/protocol/dist/account-export.js
4987
+ /**
4988
+ * The file a creator receives when they export their account: everything the relay holds about it,
4989
+ * in indented JSON, checked by this schema (`account-export.v1`). It never holds a secret and never
4990
+ * a readable content: the encrypted packets and vaults appear with their dates and sizes only, and
4991
+ * the relay cannot read them. What a product holds (documents, stories, history) lives in the
4992
+ * creator's repository and is not part of it.
4993
+ */
4994
+ const ACCOUNT_EXPORT_SCHEMA_ID = "account-export.v1";
4995
+ const Scalar = z.union([
4996
+ z.string(),
4997
+ z.number(),
4998
+ z.boolean(),
4999
+ z.null()
5000
+ ]);
5001
+ const Row = z.record(z.string(), Scalar);
5002
+ z.strictObject({
5003
+ schema: z.literal(ACCOUNT_EXPORT_SCHEMA_ID),
5004
+ generated_at: z.string(),
5005
+ /** What is not in this file, and where it is. */
5006
+ not_included: z.string(),
5007
+ account: Row,
5008
+ passkeys: z.array(Row),
5009
+ sessions: z.array(Row),
5010
+ devices: z.array(Row),
5011
+ device_prekeys: z.array(Row),
5012
+ pending_envelopes: z.array(Row),
5013
+ seen_envelopes: z.array(Row),
5014
+ vaults: z.array(Row),
5015
+ shares: z.array(Row),
5016
+ push_tokens: z.array(Row),
5017
+ exports: z.array(Row)
5018
+ });
5019
+ //#endregion
4810
5020
  //#region ../../packages/protocol/dist/skill-definition.js
4811
5021
  /** Risk level of an agent's actions. */
4812
5022
  const RISK_LEVELS = [
@@ -4952,49 +5162,93 @@ const AgentOverrideSchema = z.strictObject({
4952
5162
  voice: customisable.voice.optional()
4953
5163
  });
4954
5164
  //#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-…)" });
5165
+ //#region ../../packages/protocol/dist/autonomy.js
5166
+ /**
5167
+ * The autonomy dial of a product: three notches that set, in one gesture, how much the creator
5168
+ * keeps for themselves. The dial is a preset over settings the engine already has (the validation
5169
+ * level, the points the creator keeps, the installs mode, two permission rules); it adds no rule
5170
+ * of its own. Raising it never lifts a locked rule, the irreversible commands or the base guards
5171
+ * (secrets, pushes to the main branch), and never makes an agent validate for the creator:
5172
+ * the merge, the release and the publications are not touched by any notch.
5173
+ *
5174
+ * Everything here is data and pure functions: the machine writes the settings with the engine's own
5175
+ * writers, and the web view reads the same table to say what a notch changes.
5176
+ */
5177
+ /** The notches, from the most careful to the most free. */
5178
+ const AUTONOMY_DIALS = [
5179
+ "prudent",
5180
+ "balanced",
5181
+ "wide"
5182
+ ];
5183
+ const AutonomyDialSchema = z.enum(AUTONOMY_DIALS);
5184
+ /** What a product's settings add up to: a notch, or `custom` (set by hand or from a terminal). */
5185
+ const AUTONOMY_DIAL_STATES = [...AUTONOMY_DIALS, "custom"];
5186
+ const AutonomyDialStateSchema = z.enum(AUTONOMY_DIAL_STATES);
5187
+ /** The points of validation the dial can hand to the agents (the others are never touched). */
5188
+ const AUTONOMY_POINTS = [
5189
+ "framing",
5190
+ "architecture",
5191
+ "breakdown"
5192
+ ];
4978
5193
  /**
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
- });
5194
+ * - `none`: any value.
5195
+ * - `never_allow`: `ask` or `deny` only (the commands that erase data).
5196
+ * - `locked`: `deny` only (the push to the main branch, the force push, the secrets).
5197
+ */
5198
+ const RULE_LOCKS = {
5199
+ read_files: "none",
5200
+ edit_code: "none",
5201
+ run_checks: "none",
5202
+ install_dependency: "none",
5203
+ internet: "none",
5204
+ destructive_command: "never_allow",
5205
+ push_main: "locked",
5206
+ force_push: "locked",
5207
+ read_secrets: "locked"
5208
+ };
5209
+ const ThresholdSchema = z.int().min(3).max(50);
5210
+ /** Rule values by action: every action has one. */
5211
+ const RuleTableSchema = z.strictObject({
5212
+ read_files: z.enum(RULE_VALUES),
5213
+ edit_code: z.enum(RULE_VALUES),
5214
+ run_checks: z.enum(RULE_VALUES),
5215
+ install_dependency: z.enum(RULE_VALUES),
5216
+ internet: z.enum(RULE_VALUES),
5217
+ destructive_command: z.enum(RULE_VALUES),
5218
+ push_main: z.enum(RULE_VALUES),
5219
+ force_push: z.enum(RULE_VALUES),
5220
+ read_secrets: z.enum(RULE_VALUES)
5221
+ });
5222
+ /**
5223
+ * What a machine says of the autonomy of a product it serves. `dial`, `mode`, `installs`, `kept`
5224
+ * and `rules` are what the product's `method.yaml` records now. The engine reads these settings on
5225
+ * the main branch: when what is recorded is not in force yet, `in_force` says what is (the notch
5226
+ * and the rules that differ), and the web view does not pretend otherwise.
5227
+ */
5228
+ const ProductAutonomySchema = z.strictObject({
5229
+ dial: AutonomyDialStateSchema,
5230
+ mode: z.enum(VALIDATION_MODES),
5231
+ installs: z.enum(INSTALL_MODES),
5232
+ kept: z.array(z.enum(VALIDATION_POINTS)).max(VALIDATION_POINTS.length),
5233
+ rules: RuleTableSchema,
5234
+ in_force: z.strictObject({
5235
+ dial: AutonomyDialStateSchema,
5236
+ rules: z.partialRecord(z.enum(RULE_ACTIONS), z.enum(RULE_VALUES))
5237
+ }).optional(),
5238
+ /** Validations without a change after which a notch is proposed. */
5239
+ threshold: ThresholdSchema,
5240
+ /** Per kind of point, when the creator said « not now » to the proposal. */
5241
+ snoozed: z.partialRecord(z.enum(AUTONOMY_POINTS), z.iso.datetime()).optional()
5242
+ });
5243
+ z.strictObject({ dial: AutonomyDialSchema });
5244
+ z.strictObject({
5245
+ action: z.enum(RULE_ACTIONS),
5246
+ value: z.enum(RULE_VALUES)
4997
5247
  });
5248
+ z.strictObject({
5249
+ snooze: z.enum(AUTONOMY_POINTS).optional(),
5250
+ threshold: ThresholdSchema.optional()
5251
+ }).refine((params) => params.snooze !== void 0 || params.threshold !== void 0, { message: "a kind of point to put off, or a threshold" });
4998
5252
  //#endregion
4999
5253
  //#region ../../packages/protocol/dist/question-tool.js
5000
5254
  /**
@@ -5080,7 +5334,7 @@ const ChatProofSchema = z.strictObject({
5080
5334
  });
5081
5335
  /** The creator's words, as quoted: 1 to 4000 characters, never only blanks. */
5082
5336
  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" });
5337
+ 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
5338
  /** A tool session id as a hook gives it: printable, at most 200 characters. */
5085
5339
  const ToolSessionIdSchema = z.string().trim().min(1).max(200).regex(/^[\x21-\x7e]+$/, { message: "expected printable characters without spaces" });
5086
5340
  /**
@@ -5098,7 +5352,7 @@ const AnswerCheckpointInputSchema = z.strictObject({
5098
5352
  agent: AgentIdSchema.max(64),
5099
5353
  checkpoint: z.string().trim().toUpperCase().pipe(CheckpointIdSchema),
5100
5354
  decision: z.enum(ANSWER_DECISIONS).optional(),
5101
- option: ShortIdSchema$2.optional(),
5355
+ option: ShortIdSchema$3.optional(),
5102
5356
  note: z.string().trim().min(1).max(2e3).optional(),
5103
5357
  quote: QuoteSchema$1.optional(),
5104
5358
  session: ToolSessionIdSchema.optional(),
@@ -5159,7 +5413,7 @@ const AnswerCheckpointInputSchema = z.strictObject({
5159
5413
  const CheckpointAnsweredPayloadSchema = z.strictObject({
5160
5414
  checkpoint: CheckpointIdSchema,
5161
5415
  decision: z.enum(CHAT_DECISIONS),
5162
- option: ShortIdSchema$2.optional(),
5416
+ option: ShortIdSchema$3.optional(),
5163
5417
  note: z.string().trim().min(1).max(2e3).optional(),
5164
5418
  source: z.literal("chat"),
5165
5419
  quote: QuoteSchema$1,
@@ -5184,7 +5438,7 @@ const CheckpointAnsweredPayloadSchema = z.strictObject({
5184
5438
  const CheckpointAutoAppliedPayloadSchema = z.strictObject({
5185
5439
  checkpoint: CheckpointIdSchema,
5186
5440
  decision: z.literal("option_chosen"),
5187
- option: ShortIdSchema$2
5441
+ option: ShortIdSchema$3
5188
5442
  });
5189
5443
  /**
5190
5444
  * `payload` of `checkpoint.reopened`: an automatic application cancelled. From the chat, it
@@ -5216,81 +5470,674 @@ const ANTICIPATED_ANSWER_WINDOW_MS = 18e5;
5216
5470
  /** How long the hook keeps the creator's messages, and how recent a quoted one must be. */
5217
5471
  const CHAT_RETENTION_MS = 864e5;
5218
5472
  //#endregion
5219
- //#region ../../packages/protocol/dist/checkpoint-closed.js
5473
+ //#region ../../packages/protocol/dist/device-trust.js
5220
5474
  /**
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.
5475
+ * The devices of trust in the journal. A product's
5476
+ * registry of trusted devices is its journal: `device.paired` says "this key is trusted",
5477
+ * `device.revoked` takes it back, `device.confirmed` ends the provisional state of a device whose
5478
+ * handover is complete, `device.root_certified` records that the first phone recognised the root,
5479
+ * and `trust.dismissed` clears a `SECURITY_ROOT_CHANGED` alert. The chain of authorizations must
5480
+ * lead to the root the bridge pinned locally, outside the repository: never to an event's ULID.
5481
+ *
5482
+ * Generic on purpose: schemas and rules only, no transport, no account, no offer.
5225
5483
  */
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
5484
+ /** Event types of the devices of trust, each at version 1. */
5485
+ const DEVICE_TRUST_EVENT_TYPES = {
5486
+ paired: "device.paired",
5487
+ revoked: "device.revoked",
5488
+ rootCertified: "device.root_certified",
5489
+ confirmed: "device.confirmed",
5490
+ dismissed: "trust.dismissed"
5491
+ };
5248
5492
  /**
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.
5493
+ * The types the engine writes as a system actor (the bridge, or a creator's terminal for the
5494
+ * first ones): refused in any write by an agent. `trust.dismissed` is the creator's (signed by
5495
+ * the creator's key).
5255
5496
  */
5256
- /** Event type of a point decided by an agent, at version 1. */
5257
- const CHECKPOINT_DECIDED_EVENT_TYPES = { decided: "checkpoint.decided" };
5497
+ const DEVICE_TRUST_SYSTEM_EVENT_TYPES = /* @__PURE__ */ new Set([
5498
+ DEVICE_TRUST_EVENT_TYPES.paired,
5499
+ DEVICE_TRUST_EVENT_TYPES.revoked,
5500
+ DEVICE_TRUST_EVENT_TYPES.rootCertified,
5501
+ DEVICE_TRUST_EVENT_TYPES.confirmed
5502
+ ]);
5503
+ new Set(Object.values(DEVICE_TRUST_EVENT_TYPES));
5504
+ /** The kinds of device a `device.paired` registers (the creator's key has its own events). */
5505
+ const PAIRED_DEVICE_KINDS = [
5506
+ "machine",
5507
+ "phone",
5508
+ "browser"
5509
+ ];
5510
+ /** `machine:`, `human:`, `phone:` or `browser:` then 16 base64url characters. */
5511
+ const DEVICE_ID_PATTERN = /^(machine|human|phone|browser):[A-Za-z0-9_-]{16}$/;
5512
+ /** The identifier of a device `device.paired` registers. */
5513
+ const PAIRED_DEVICE_ID_PATTERN = /^(machine|phone|browser):[A-Za-z0-9_-]{16}$/;
5514
+ /** The identifier of a bridge. */
5515
+ const BRIDGE_ID_PATTERN = /^machine:[A-Za-z0-9_-]{16}$/;
5516
+ /** The kind of identifier (`machine`, `human`, `phone`, `browser`), or undefined when malformed. */
5517
+ function deviceKindOf(device) {
5518
+ if (!DEVICE_ID_PATTERN.test(device)) return void 0;
5519
+ return device.slice(0, device.indexOf(":"));
5520
+ }
5521
+ /** The algorithms of the linked devices (the `webauthn-*` ones serve the terminal's key only). */
5522
+ const DEVICE_SIG_ALG = "ed25519";
5523
+ const DEVICE_ENC_ALG = "x25519";
5524
+ /** 32 bytes in base64url: 43 characters, the last one carrying 4 bits only. */
5525
+ const PUBLIC_KEY_32 = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
5526
+ /** 16 random bytes in base64url: 22 characters, the last one carrying 2 bits only. */
5527
+ const RANDOM_16 = /^[A-Za-z0-9_-]{21}[AQgw]$/;
5528
+ /** `sha256:` then the 32-byte digest in base64url (an event's hash). */
5529
+ const TRUST_EVENT_HASH_PATTERN = /^sha256:[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
5530
+ const publicKey32 = z.string().regex(PUBLIC_KEY_32).refine(isCanonicalBase64url);
5531
+ const utcDatetime$1 = z.iso.datetime();
5532
+ /** A public key of a device with its algorithm: `{ alg, key }`. */
5533
+ const DeviceSigKeySchema = z.strictObject({
5534
+ alg: z.literal(DEVICE_SIG_ALG),
5535
+ key: publicKey32
5536
+ });
5537
+ const DeviceEncKeySchema = z.strictObject({
5538
+ alg: z.literal(DEVICE_ENC_ALG),
5539
+ key: publicKey32
5540
+ });
5258
5541
  /**
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.
5542
+ * Every signed structure of trust carries its label `t` (type and version), and a key signs the
5543
+ * types it knows only: an authorization is never taken for an order, nor a relay's challenge for
5544
+ * an authorization. The domain separation stops a signature made for one purpose from
5545
+ * serving another.
5262
5546
  */
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
- });
5547
+ const TRUST_SIGNATURE_TAGS = {
5548
+ devicePaired: "device.paired.auth/v1",
5549
+ deviceRevoked: "device.revoked.auth/v1",
5550
+ pairOffer: "pair.offer/v1",
5551
+ rootCertified: "device.root_certified.auth/v1",
5552
+ message: "msg.inner/v1",
5553
+ linkWelcome: "link.welcome/v1",
5554
+ vaultBlob: "vault.blob/v1"
5555
+ };
5556
+ /** True for a label this engine knows. */
5557
+ function isTrustSignatureTag(tag) {
5558
+ return Object.values(TRUST_SIGNATURE_TAGS).includes(tag);
5272
5559
  }
5273
5560
  /**
5274
- * Input of `decide_checkpoint`: the agent that decides, the point, its decision and the reason.
5275
- * `option` goes with `option_chosen` only.
5561
+ * The order of a trusted device that offered a pairing (`pair.offer`): one rendezvous
5562
+ * `x`, valid until `expires_at`, for one device of `kind`. The bridge writes `device.paired` on
5563
+ * its authority, once.
5276
5564
  */
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. */
5285
- const CheckpointDecidedPayloadSchema = z.strictObject({
5286
- checkpoint: CheckpointIdSchema,
5287
- decision: z.enum(DECIDE_CHOICES),
5288
- option: ShortIdSchema$1.optional(),
5289
- reason: ReasonSchema$1
5290
- }).superRefine(checkOption);
5291
- //#endregion
5292
- //#region ../../packages/protocol/dist/ci.js
5293
- /**
5565
+ const PairOfferSchema = z.strictObject({
5566
+ /** The rendezvous: 16 random bytes in base64url, or the 8-symbol address of a short code. */
5567
+ x: z.union([z.string().regex(RANDOM_16).refine(isCanonicalBase64url), z.string().regex(/^[0-9A-HJKMNP-TV-Z]{8}$/)]),
5568
+ expires_at: utcDatetime$1
5569
+ });
5570
+ /**
5571
+ * The proof that the creator wanted this: the signature, by a device already trusted (or by the
5572
+ * root, from the command run on the root machine), of the record of the event (`order` absent) or
5573
+ * of the signed order `pair.offer` (`order` present).
5574
+ */
5575
+ const AuthorizationSchema = z.strictObject({
5576
+ signer: z.string().regex(DEVICE_ID_PATTERN),
5577
+ alg: z.literal(DEVICE_SIG_ALG),
5578
+ sig: Ed25519SignatureSchema,
5579
+ order: PairOfferSchema.optional()
5580
+ });
5581
+ /** `payload` of `device.paired` v1. */
5582
+ const DevicePairedPayloadSchema = z.strictObject({
5583
+ device: z.string().regex(PAIRED_DEVICE_ID_PATTERN),
5584
+ kind: z.enum(PAIRED_DEVICE_KINDS),
5585
+ sig: DeviceSigKeySchema,
5586
+ enc: DeviceEncKeySchema,
5587
+ /** The bridge of the perimeter: the authorization holds in the journals this bridge writes. */
5588
+ bridge: z.string().regex(BRIDGE_ID_PATTERN),
5589
+ /** True until the handover is complete (`device.confirmed`): the bridge alone may revoke it. */
5590
+ provisional: z.boolean(),
5591
+ issued_at: utcDatetime$1,
5592
+ authorization: AuthorizationSchema
5593
+ }).refine((payload) => payload.device.startsWith(`${payload.kind}:`), {
5594
+ path: ["kind"],
5595
+ message: "the kind is the prefix of the device"
5596
+ });
5597
+ /** `payload` of `device.revoked` v1. */
5598
+ const DeviceRevokedPayloadSchema = z.strictObject({
5599
+ device: z.string().regex(PAIRED_DEVICE_ID_PATTERN),
5600
+ /**
5601
+ * For a machine that writes to the journal: the hash of its last event the author knew. The
5602
+ * projection accepts of a revoked machine only the ancestors of this head (and of the heads of
5603
+ * the other revocations of it). Null for a device that writes nothing.
5604
+ */
5605
+ chain_head: z.string().regex(TRUST_EVENT_HASH_PATTERN).nullable(),
5606
+ issued_at: utcDatetime$1,
5607
+ author: z.string().regex(DEVICE_ID_PATTERN),
5608
+ authorization: z.strictObject({
5609
+ alg: z.literal(DEVICE_SIG_ALG),
5610
+ sig: Ed25519SignatureSchema
5611
+ })
5612
+ }).refine((payload) => payload.chain_head === null || payload.device.startsWith("machine:"), {
5613
+ path: ["chain_head"],
5614
+ message: "only a machine has a chain head"
5615
+ });
5616
+ /** `payload` of `device.root_certified` v1: the first phone recognises the root. */
5617
+ const DeviceRootCertifiedPayloadSchema = z.strictObject({
5618
+ root: z.strictObject({
5619
+ device: z.string().regex(DEVICE_ID_PATTERN),
5620
+ key: publicKey32
5621
+ }),
5622
+ issued_at: utcDatetime$1,
5623
+ authorization: AuthorizationSchema
5624
+ });
5625
+ /** `payload` of `device.confirmed` v1: the handover of the device is complete. */
5626
+ const DeviceConfirmedPayloadSchema = z.strictObject({
5627
+ device: z.string().regex(PAIRED_DEVICE_ID_PATTERN),
5628
+ issued_at: utcDatetime$1
5629
+ });
5630
+ /** `payload` of `trust.dismissed` v1: the events (their hashes) the creator dismisses. */
5631
+ const TrustDismissedPayloadSchema = z.strictObject({
5632
+ events: z.array(z.string().regex(TRUST_EVENT_HASH_PATTERN)).min(1).max(16),
5633
+ issued_at: utcDatetime$1
5634
+ });
5635
+ /** The pinned root of a product, kept locally outside the repository (written by `pair`). */
5636
+ const RootPinSchema = z.strictObject({
5637
+ device: z.string().regex(/^(machine|human):[A-Za-z0-9_-]{16}$/),
5638
+ public_key: publicKey32
5639
+ });
5640
+ /**
5641
+ * What the authorization of a `device.paired` signs: its payload without `authorization`, plus the
5642
+ * label. Independent of the product and of `prev`: the bridge writes it in each journal it serves,
5643
+ * but cannot make one without the signer.
5644
+ */
5645
+ function devicePairedRecord(payload) {
5646
+ return {
5647
+ t: TRUST_SIGNATURE_TAGS.devicePaired,
5648
+ device: payload.device,
5649
+ kind: payload.kind,
5650
+ sig: payload.sig,
5651
+ enc: payload.enc,
5652
+ bridge: payload.bridge,
5653
+ provisional: payload.provisional,
5654
+ issued_at: payload.issued_at
5655
+ };
5656
+ }
5657
+ /** What the signed order `pair.offer` covers. */
5658
+ function pairOfferRecord(input) {
5659
+ return {
5660
+ t: TRUST_SIGNATURE_TAGS.pairOffer,
5661
+ kind: input.kind,
5662
+ bridge: input.bridge,
5663
+ x: input.order.x,
5664
+ expires_at: input.order.expires_at
5665
+ };
5666
+ }
5667
+ /** What the authorization of a `device.revoked` signs. */
5668
+ function deviceRevokedRecord(payload) {
5669
+ return {
5670
+ t: TRUST_SIGNATURE_TAGS.deviceRevoked,
5671
+ device: payload.device,
5672
+ chain_head: payload.chain_head,
5673
+ issued_at: payload.issued_at,
5674
+ author: payload.author
5675
+ };
5676
+ }
5677
+ /** What the authorization of a `device.root_certified` signs. */
5678
+ function rootCertifiedRecord(payload) {
5679
+ return {
5680
+ t: TRUST_SIGNATURE_TAGS.rootCertified,
5681
+ root: payload.root,
5682
+ issued_at: payload.issued_at
5683
+ };
5684
+ }
5685
+ //#endregion
5686
+ //#region ../../packages/protocol/dist/checkpoint-web.js
5687
+ /**
5688
+ * Event types of the answers given from the web view, at version 1. A reversible point's answer
5689
+ * is first held (`held`) for the length of the undo window, with the point still open for everyone;
5690
+ * then it is either taken back (`holdCancelled`) or carried out (`answered`). An answer to a point
5691
+ * that cannot be undone is carried out at once. A held answer whose carrying out the machine could
5692
+ * not do (the merge of a story that the forge refuses) is released (`holdFailed`): the point stays
5693
+ * open and says why.
5694
+ */
5695
+ const WEB_ANSWER_EVENT_TYPES = {
5696
+ answered: "checkpoint.web_answered",
5697
+ held: "checkpoint.web_held",
5698
+ holdCancelled: "checkpoint.web_hold_cancelled",
5699
+ holdFailed: "checkpoint.web_hold_failed"
5700
+ };
5701
+ /** How long the machine holds a reversible answer before it carries it out: the creator may undo it. */
5702
+ const WEB_ANSWER_WINDOW_MS = 1e4;
5703
+ /**
5704
+ * How long after its window a held answer is still waited for. Past it, the machine did not carry
5705
+ * the answer out (it is off, or the journal refuses it): the point is open again for a new answer.
5706
+ */
5707
+ const WEB_ANSWER_STALE_MS = 15e3;
5708
+ /** The longest note the creator types with a decision, like a resolution's note. */
5709
+ const MAX_WEB_ANSWER_NOTE_LENGTH = 2e3;
5710
+ /** Where an answer carried by a signed order came from: a browser (`web`) or a phone (`mobile`). */
5711
+ const ANSWER_SOURCES = ["web", "mobile"];
5712
+ /**
5713
+ * The source of an answer given by `device`, drawn from the kind of its identifier (a browser's
5714
+ * `browser:`, a phone's `phone:`), which the journal registered when the device was paired. Undefined
5715
+ * for any other device: a machine never answers this way.
5716
+ */
5717
+ function answerSourceOf(device) {
5718
+ if (device.startsWith("browser:")) return "web";
5719
+ if (device.startsWith("phone:")) return "mobile";
5720
+ }
5721
+ 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" });
5722
+ function checkAnswer(value, context) {
5723
+ if (value.decision === "option_chosen" !== (value.option !== void 0)) context.addIssue({
5724
+ code: "custom",
5725
+ path: ["option"],
5726
+ message: "an option goes with option_chosen, and only with it"
5727
+ });
5728
+ if (NOTE_REQUIRED.has(value.decision) && value.note === void 0) context.addIssue({
5729
+ code: "custom",
5730
+ path: ["note"],
5731
+ message: "this decision needs a note"
5732
+ });
5733
+ }
5734
+ z.strictObject({
5735
+ checkpoint: CheckpointIdSchema,
5736
+ decision: z.enum(CHAT_DECISIONS),
5737
+ option: ShortIdSchema$2.optional(),
5738
+ note: z.string().trim().min(1).max(MAX_WEB_ANSWER_NOTE_LENGTH).optional()
5739
+ }).superRefine(checkAnswer);
5740
+ /** Refuses a source that is not the one the device's kind gives. */
5741
+ function checkSource(value, context) {
5742
+ if (answerSourceOf(value.device) !== value.source) context.addIssue({
5743
+ code: "custom",
5744
+ path: ["source"],
5745
+ message: "the source is the one the device's kind gives"
5746
+ });
5747
+ }
5748
+ /**
5749
+ * `payload` of `checkpoint.web_answered`: the point, the decision, the order that carried it and
5750
+ * the browser or phone device that signed it. The assurance (`device`) is drawn from the event's
5751
+ * type by the projection, never declared; the source must be the one the device's kind gives.
5752
+ */
5753
+ const CheckpointWebAnsweredPayloadSchema = z.strictObject({
5754
+ checkpoint: CheckpointIdSchema,
5755
+ decision: z.enum(CHAT_DECISIONS),
5756
+ option: ShortIdSchema$2.optional(),
5757
+ note: z.string().trim().min(1).max(MAX_WEB_ANSWER_NOTE_LENGTH).optional(),
5758
+ source: z.enum(ANSWER_SOURCES),
5759
+ order: UlidSchema,
5760
+ device: z.string().regex(PAIRED_DEVICE_ID_PATTERN)
5761
+ }).superRefine(checkAnswer).superRefine(checkSource);
5762
+ z.strictObject({ checkpoint: CheckpointIdSchema });
5763
+ /**
5764
+ * `payload` of `checkpoint.web_held`: an answer the machine holds for `WEB_ANSWER_WINDOW_MS`. It
5765
+ * is the answer of `checkpoint.web_answered`, which the machine writes when the window is over; the
5766
+ * point stays open until then.
5767
+ */
5768
+ const CheckpointWebHeldPayloadSchema = CheckpointWebAnsweredPayloadSchema;
5769
+ /**
5770
+ * `payload` of `checkpoint.web_hold_cancelled`: the held answer of the order `held` taken back by
5771
+ * the order `order`, signed by the browser device `device`. The point was never answered.
5772
+ */
5773
+ const CheckpointWebHoldCancelledPayloadSchema = z.strictObject({
5774
+ checkpoint: CheckpointIdSchema,
5775
+ held: UlidSchema,
5776
+ order: UlidSchema,
5777
+ device: z.string().regex(PAIRED_DEVICE_ID_PATTERN)
5778
+ });
5779
+ /** The longest code and reason a failed carrying out records: short tokens, never a message. */
5780
+ const FAILURE_TOKEN = /^[A-Za-z][A-Za-z0-9_.]{0,47}$/;
5781
+ /**
5782
+ * `payload` of `checkpoint.web_hold_failed`: the held answer of the order `held` that the machine
5783
+ * could not carry out (`code` is the engine's error code, `reason` its short reason when it has
5784
+ * one, `merge_failed` and `head_moved` for instance). Nothing was decided and the point is open
5785
+ * again. Neither a message nor an address nor a token is recorded.
5786
+ */
5787
+ const CheckpointWebHoldFailedPayloadSchema = z.strictObject({
5788
+ checkpoint: CheckpointIdSchema,
5789
+ held: UlidSchema,
5790
+ device: z.string().regex(PAIRED_DEVICE_ID_PATTERN),
5791
+ code: z.string().regex(FAILURE_TOKEN),
5792
+ reason: z.string().regex(FAILURE_TOKEN).optional()
5793
+ });
5794
+ //#endregion
5795
+ //#region ../../packages/protocol/dist/push.js
5796
+ /**
5797
+ * Notifications without content. A notification carries an opaque identifier, a category and a
5798
+ * level of urgency, and nothing else: the relay never learns a product's name, a title or a text.
5799
+ * The machine decides what is urgent (it knows the content); the relay only forwards, to the
5800
+ * phones of the account, through the push services of the platforms.
5801
+ */
5802
+ /** The platforms a phone can be reached on: APNs (iOS) and FCM (Android), directly. */
5803
+ const PUSH_PLATFORMS = ["ios", "android"];
5804
+ /** What a notification is about, in the words of the product (never a product's own text). */
5805
+ const NOTIFY_CATEGORIES = [
5806
+ "decision",
5807
+ "permission",
5808
+ "merge"
5809
+ ];
5810
+ /** How hard a notification interrupts. */
5811
+ const NOTIFY_URGENCIES = ["urgent", "normal"];
5812
+ const PushTokenSchema = z.string().min(16).max(4096).regex(/^[\x21-\x7e]+$/);
5813
+ /** The opaque identifier of a notification: 43 characters of base64url (a digest). */
5814
+ const NotifyIdSchema = z.string().regex(/^[A-Za-z0-9_-]{43}$/);
5815
+ /**
5816
+ * What a notification carries, in total. The relay builds the platform message from this and from
5817
+ * a fixed text of its own; nothing of the product is in it.
5818
+ */
5819
+ const PushNoticeSchema = z.strictObject({
5820
+ id: NotifyIdSchema,
5821
+ category: z.enum(NOTIFY_CATEGORIES),
5822
+ urgency: z.enum(NOTIFY_URGENCIES)
5823
+ });
5824
+ /** The surfaces of the creator that can be notified (the watch comes with its own app). */
5825
+ const NOTIFY_SURFACES = ["web", "mobile"];
5826
+ /** What a surface rings for: only the urgent, everything that comes back to the creator, nothing. */
5827
+ const SURFACE_CHOICES = [
5828
+ "urgent",
5829
+ "all",
5830
+ "none"
5831
+ ];
5832
+ /** A time zone name as the platforms give it (`Europe/Paris`): bounded, no free text. */
5833
+ const TimeZoneSchema = z.string().regex(/^[A-Za-z][A-Za-z0-9_+\-/]{0,63}$/);
5834
+ const CategoriesSchema = z.array(z.enum(NOTIFY_CATEGORIES)).max(NOTIFY_CATEGORIES.length).refine((list) => new Set(list).size === list.length, { message: "duplicate category" });
5835
+ const MinuteOfDaySchema = z.number().int().min(0).max(1439);
5836
+ /**
5837
+ * What the creator chose about notifications. It holds no product text and no content: categories,
5838
+ * hours and surfaces. The relay keeps it in clear because it applies it (it never sees more than an
5839
+ * opaque notice); the same document is shown and changed from every surface.
5840
+ */
5841
+ const NotifyPrefsSchema = z.strictObject({
5842
+ /** The categories that notify at all. */
5843
+ types: CategoriesSchema,
5844
+ /** Active on one surface: the others stay quiet. Off: each surface follows its own choice. */
5845
+ presence: z.boolean(),
5846
+ surfaces: z.strictObject({
5847
+ web: z.enum(SURFACE_CHOICES),
5848
+ mobile: z.enum(SURFACE_CHOICES)
5849
+ }),
5850
+ quiet: z.strictObject({
5851
+ enabled: z.boolean(),
5852
+ /** Minute of the day the silence starts and ends, in `tz`. Equal values mean no silence. */
5853
+ from: MinuteOfDaySchema,
5854
+ to: MinuteOfDaySchema,
5855
+ tz: TimeZoneSchema,
5856
+ /** The urgent categories that still ring during the silence: none by default. */
5857
+ pass: CategoriesSchema
5858
+ })
5859
+ });
5860
+ [...NOTIFY_CATEGORIES];
5861
+ /** The two briefings: the morning one (what waits) and the evening one (the day, then the calm decisions). */
5862
+ const BRIEFING_PARTS = ["morning", "evening"];
5863
+ /** The request shape (without the identifier, the version and the product, which the file reading adds). */
5864
+ const BriefingRequestFields = {
5865
+ op: z.literal("briefing"),
5866
+ /** Absent: the morning briefing. */
5867
+ part: z.enum(BRIEFING_PARTS).optional()
5868
+ };
5869
+ const Count$2 = z.int().min(0).max(Number.MAX_SAFE_INTEGER);
5870
+ /** What the creator can do with a point from a phone: every decision, none to approve, or none at all. */
5871
+ const BRIEFING_ANSWERING = [
5872
+ "full",
5873
+ "no_approval",
5874
+ "elsewhere"
5875
+ ];
5876
+ const BriefingOptionSchema = z.strictObject({
5877
+ id: z.string().min(1).max(64),
5878
+ label: z.string().min(1).max(100),
5879
+ recommended: z.boolean()
5880
+ });
5881
+ /**
5882
+ * What the listening version of a point is made from: the fields its agent already wrote when it
5883
+ * opened the point (the card's effect, its first risk and its reason, a deliverable's short
5884
+ * version), as plain text, or the start of the summary when no card was written. The device turns
5885
+ * them into sentences of its own catalog; the machine never writes a sentence, and nothing is
5886
+ * asked of a model. Every field is untrusted text written by an agent.
5887
+ */
5888
+ const BriefingListenSchema = z.strictObject({
5889
+ /** What the point changes for the people who use the product (the card's `effect`). */
5890
+ effect: z.string().min(1).max(300).optional(),
5891
+ /** The start of the summary: only when the agent wrote no card. */
5892
+ summary: z.string().min(1).max(600).optional(),
5893
+ /** The first risk of the card. */
5894
+ risk: z.string().min(1).max(200).optional(),
5895
+ /** Why the agent proposes this, and the alternative it set aside. */
5896
+ reason: z.string().min(1).max(200).optional(),
5897
+ rejected: z.string().min(1).max(200).optional(),
5898
+ /** A deliverable's short version, written by its agent for about fifteen seconds of reading. */
5899
+ short: z.string().min(1).max(600).optional()
5900
+ });
5901
+ const BriefingItemSchema = z.strictObject({
5902
+ /** The checkpoint's identifier: the device finds the point on its own board with it. */
5903
+ id: z.string().min(1).max(64),
5904
+ type: z.string().min(1).max(32),
5905
+ risk: z.enum([
5906
+ "low",
5907
+ "medium",
5908
+ "high"
5909
+ ]),
5910
+ /** True when the point holds something back or sits at a high risk (the rule of the notifications). */
5911
+ urgent: z.boolean(),
5912
+ /** True when the point cannot be taken back once decided. */
5913
+ irreversible: z.boolean(),
5914
+ answering: z.enum(BRIEFING_ANSWERING),
5915
+ /** The agent that opened the point, when one did. */
5916
+ agent: z.string().min(1).max(64).optional(),
5917
+ title: z.string().min(1).max(120),
5918
+ story: z.string().min(1).max(32).optional(),
5919
+ options: z.array(BriefingOptionSchema).max(6),
5920
+ /** The fields the listening version is made from; absent when the point has no text to read. */
5921
+ listen: BriefingListenSchema.optional(),
5922
+ /** Set on a story's merge point: the story and the branch it lands on. */
5923
+ merge: z.strictObject({
5924
+ story: z.string().min(1).max(32),
5925
+ base: z.string().min(1).max(100)
5926
+ }).optional()
5927
+ });
5928
+ /**
5929
+ * The day of a product, in the evening briefing: what was delivered since midnight on the machine's
5930
+ * clock, how many points still wait at a high risk or holding something back (they are counted,
5931
+ * never listed: they are not for the evening), and what the day cost when the bridge could tell.
5932
+ */
5933
+ const BriefingDaySchema = z.strictObject({
5934
+ /** Stories merged today. */
5935
+ merged: Count$2,
5936
+ /** Publications the creator validated today. */
5937
+ published: Count$2,
5938
+ /** Decisions the creator made today. */
5939
+ decided: Count$2,
5940
+ /** Points that wait and are urgent: not in `items`. */
5941
+ urgent: Count$2,
5942
+ /** What the measured sessions of the day cost; absent when the costs cannot be read. */
5943
+ cost: z.strictObject({
5944
+ /** Dollars of the models the table prices; absent when no model of the day has a price. */
5945
+ usd: z.number().min(0).max(1e8).optional(),
5946
+ /** `real`, `partial` or `unknown`: a device shows another value as `unknown`. */
5947
+ nature: z.string().min(1).max(16),
5948
+ tokens: Count$2
5949
+ }).optional()
5950
+ });
5951
+ const BriefingDataSchema = z.strictObject({
5952
+ op: z.literal("briefing"),
5953
+ /** Which briefing this is; absent in a morning briefing. */
5954
+ part: z.enum(BRIEFING_PARTS).optional(),
5955
+ /** The day of the product; present in an evening briefing only. */
5956
+ day: BriefingDaySchema.optional(),
5957
+ /** Milliseconds since the epoch, on the machine's clock. */
5958
+ composed_at: Count$2,
5959
+ /** What moved in the last 24 hours (in the evening briefing, since midnight). */
5960
+ progress: z.strictObject({
5961
+ merged: Count$2,
5962
+ decided: Count$2
5963
+ }),
5964
+ /** How many points wait for the creator (held answers excluded), whatever `items` lists. */
5965
+ waiting: Count$2,
5966
+ items: z.array(BriefingItemSchema).max(12)
5967
+ });
5968
+ //#endregion
5969
+ //#region ../../packages/protocol/dist/budget.js
5970
+ /**
5971
+ * Budgets: each story's ceiling comes from the journal (the last sprint approved that
5972
+ * holds it, or the creator's answer to a budget checkpoint); its cost from the agents' declared
5973
+ * costs (`cost.reported`, encrypted). When a declared cost reaches the alert ratio, or goes over
5974
+ * the ceiling, `core` writes `budget.threshold_reached` (no amount of cost in it), right after
5975
+ * that cost in the same write. Over the ceiling, with `on_exceed: block`, it opens a budget
5976
+ * checkpoint in between, and the story's write tools answer `BUDGET_EXCEEDED` until the creator
5977
+ * answers it.
5978
+ */
5979
+ /** Event types of budgets, each at version 1. */
5980
+ const BUDGET_EVENT_TYPES = { thresholdReached: "budget.threshold_reached" };
5981
+ /** The thresholds a story's cost may reach: the alert ratio, then the ceiling itself. */
5982
+ const BUDGET_LEVELS = ["alert", "exceeded"];
5983
+ /** The largest ceiling, in US dollars (raising a ceiling stops there). */
5984
+ const MAX_BUDGET_USD = 1e9;
5985
+ /** The prefix of a budget checkpoint's title, before the story. */
5986
+ const BUDGET_TITLE_PREFIX = "Budget exceeded: ";
5987
+ /**
5988
+ * The source of a ceiling: the sprint (`S-…`) whose approval set it, or the budget checkpoint
5989
+ * (`G-…`) whose answer did. A threshold counts only for the ceiling it names.
5990
+ */
5991
+ 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-…)" });
5992
+ /**
5993
+ * `payload` of `budget.threshold_reached`: the level reached, the ceiling it was measured against
5994
+ * (its amount and its source, never the cost), the settings in force, and the budget checkpoint
5995
+ * opened just before it in the same write (`exceeded` under `block` only; absent when one is
5996
+ * already open).
5997
+ */
5998
+ const BudgetThresholdReachedPayloadSchema = z.strictObject({
5999
+ level: z.enum(BUDGET_LEVELS),
6000
+ budget_usd: z.number().finite().min(0).max(MAX_BUDGET_USD),
6001
+ ceiling: CeilingIdSchema,
6002
+ alert_ratio: z.number().finite().min(MIN_ALERT_RATIO).max(1),
6003
+ on_exceed: z.enum(BUDGET_EXCEED_MODES),
6004
+ checkpoint: CheckpointIdSchema.optional()
6005
+ }).superRefine((value, context) => {
6006
+ if (value.checkpoint !== void 0 && (value.level !== "exceeded" || value.on_exceed !== "block")) context.addIssue({
6007
+ code: "custom",
6008
+ path: ["checkpoint"],
6009
+ message: "a checkpoint goes only with an exceeded level under block"
6010
+ });
6011
+ });
6012
+ //#endregion
6013
+ //#region ../../packages/protocol/dist/chain.js
6014
+ /** The event the bridge writes when it takes an instruction of the creator for an agent. */
6015
+ const CHAIN_EVENT_TYPES = { instructionGiven: "agent.instruction_given" };
6016
+ /** The longest instruction, in characters, and how many wait for an agent that is not working. */
6017
+ const CHAIN_INSTRUCTION_MAX_CHARS = 4e3;
6018
+ z.strictObject({
6019
+ budget_usd: z.number().finite().positive().max(MAX_BUDGET_USD).optional(),
6020
+ max_steps: z.number().int().min(1).max(100).optional()
6021
+ });
6022
+ const AgentTargetSchema = AgentIdSchema.max(64);
6023
+ z.strictObject({
6024
+ agent: AgentTargetSchema.optional(),
6025
+ when: z.enum(["now", "after_step"]).optional(),
6026
+ /**
6027
+ * The creator takes the hand on the agent. The agent is stopped cleanly at once (never after
6028
+ * its step), its session is kept, and the team stays in the creator's hands until they take it
6029
+ * up again. `chat`: the creator talks to the agent from their device, one message at a time
6030
+ * (`chain.say`); `terminal`: they reopen the session in their own terminal on the machine.
6031
+ */
6032
+ hand: z.enum(["chat", "terminal"]).optional()
6033
+ }).refine((value) => value.hand === void 0 || value.when !== "after_step", { message: "the creator takes the session over at once, not after the step" });
6034
+ z.strictObject({
6035
+ agent: AgentTargetSchema.optional(),
6036
+ close_session: z.boolean().optional()
6037
+ });
6038
+ z.strictObject({
6039
+ text: z.string().min(1).max(CHAIN_INSTRUCTION_MAX_CHARS).refine((value) => value.trim().length > 0, { message: "the message is empty" }),
6040
+ close_session: z.boolean().optional()
6041
+ });
6042
+ z.strictObject({
6043
+ text: z.string().min(1).max(CHAIN_INSTRUCTION_MAX_CHARS).refine((value) => value.trim().length > 0, { message: "the message is empty" }),
6044
+ input: z.enum(["text", "voice"]).optional()
6045
+ });
6046
+ z.strictObject({ agent: AgentTargetSchema.optional() });
6047
+ z.strictObject({
6048
+ agent: AgentTargetSchema,
6049
+ text: z.string().min(1).max(CHAIN_INSTRUCTION_MAX_CHARS).refine((value) => value.trim().length > 0, { message: "the instruction is empty" })
6050
+ });
6051
+ /** `payload` of `agent.instruction_given`: the creator's instruction, kept encrypted in the journal. */
6052
+ const InstructionGivenPayloadSchema = z.strictObject({
6053
+ /** Who the creator spoke to (an agent, or Noah). */
6054
+ to: AgentTargetSchema,
6055
+ /** The agent the instruction goes to when Noah routed it. */
6056
+ routed_to: AgentTargetSchema.optional(),
6057
+ text: z.string().min(1).max(CHAIN_INSTRUCTION_MAX_CHARS),
6058
+ /** `now`: the agent that works is redirected; `queued`: the agent gets it when it next starts. */
6059
+ delivery: z.enum([
6060
+ "now",
6061
+ "queued",
6062
+ "chat"
6063
+ ])
6064
+ });
6065
+ //#endregion
6066
+ //#region ../../packages/protocol/dist/checkpoint-closed.js
6067
+ /**
6068
+ * A point closed by the engine itself: when what a checkpoint asks about is already
6069
+ * settled, nobody has to answer it. The engine writes `checkpoint.closed` with the reason, and the
6070
+ * projection checks the reason against the journal (a point that is still waiting for something is
6071
+ * never closed). The creator is never shown a point whose object is settled.
6072
+ */
6073
+ /** Event type of the closing of a checkpoint by the engine, at version 1. */
6074
+ const CHECKPOINT_CLOSED_EVENT_TYPES = { closed: "checkpoint.closed" };
6075
+ /** `payload` of `checkpoint.closed`: the checkpoint, why, and the newer one for `replaced`. */
6076
+ const CheckpointClosedPayloadSchema = z.strictObject({
6077
+ checkpoint: CheckpointIdSchema,
6078
+ reason: z.enum([
6079
+ "story_merged",
6080
+ "review_redone",
6081
+ "replaced",
6082
+ "step_skipped",
6083
+ "block_lifted",
6084
+ "worktree_gone",
6085
+ "lapsed"
6086
+ ]),
6087
+ /** The checkpoint that replaces it: with `replaced`, and only with it. */
6088
+ ref: CheckpointIdSchema.optional()
6089
+ }).refine((payload) => payload.reason === "replaced" === (payload.ref !== void 0), {
6090
+ message: "a ref goes with replaced, and only with it",
6091
+ path: ["ref"]
6092
+ });
6093
+ //#endregion
6094
+ //#region ../../packages/protocol/dist/checkpoint-decided.js
6095
+ /**
6096
+ * A point decided by the agent responsible for it. The creator keeps for
6097
+ * themselves the points they chose (`validation.points` of `method.yaml`); any other point is
6098
+ * decided by an agent, which says why. The decision is written as the agent (`actor.kind: agent`,
6099
+ * signed by the machine key), never as the creator: its source and its assurance are `agent`, so
6100
+ * the history, the morning summary and every surface always tell it apart from an answer of the
6101
+ * creator, and the creator can reopen it at any time.
6102
+ */
6103
+ /** Event type of a point decided by an agent, at version 1. */
6104
+ const CHECKPOINT_DECIDED_EVENT_TYPES = { decided: "checkpoint.decided" };
6105
+ /**
6106
+ * What `decide_checkpoint` may be given: a decision, or `escalate`, the agent handing the point back
6107
+ * to the creator with its reason when it cannot or should not decide it (a merge the forge refuses
6108
+ * and nobody can fix, a point it finds beyond its remit). The point then waits for the creator.
6109
+ */
6110
+ const DECIDE_CHOICES = [...CHAT_DECISIONS, "escalate"];
6111
+ 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" });
6112
+ const ReasonSchema$1 = z.string().trim().min(1).max(1e3);
6113
+ function checkOption(value, context) {
6114
+ if (value.decision === "option_chosen" !== (value.option !== void 0)) context.addIssue({
6115
+ code: "custom",
6116
+ path: ["option"],
6117
+ message: "an option goes with option_chosen, and only with it"
6118
+ });
6119
+ }
6120
+ /**
6121
+ * Input of `decide_checkpoint`: the agent that decides, the point, its decision and the reason.
6122
+ * `option` goes with `option_chosen` only.
6123
+ */
6124
+ const DecideCheckpointInputSchema = z.strictObject({
6125
+ agent: AgentIdSchema.max(64),
6126
+ checkpoint: z.string().trim().toUpperCase().pipe(CheckpointIdSchema),
6127
+ decision: z.enum(DECIDE_CHOICES),
6128
+ option: ShortIdSchema$1.optional(),
6129
+ reason: ReasonSchema$1
6130
+ }).superRefine(checkOption);
6131
+ /** `payload` of `checkpoint.decided`: the point, the decision and the agent's reason. */
6132
+ const CheckpointDecidedPayloadSchema = z.strictObject({
6133
+ checkpoint: CheckpointIdSchema,
6134
+ decision: z.enum(DECIDE_CHOICES),
6135
+ option: ShortIdSchema$1.optional(),
6136
+ reason: ReasonSchema$1
6137
+ }).superRefine(checkOption);
6138
+ //#endregion
6139
+ //#region ../../packages/protocol/dist/ci.js
6140
+ /**
5294
6141
  * The CI of a story: `core` reads the checks of the story's pull request through the
5295
6142
  * forge's command-line tool (`gh`, `glab`), then writes `story.ci_reported`. A failed CI sends the
5296
6143
  * story back to its agent; a merge checkpoint waits for a green CI and a done review. When the CI
@@ -5357,6 +6204,364 @@ const CheckCiInputSchema = z.strictObject({
5357
6204
  forge: z.enum(CI_FORGES).optional()
5358
6205
  });
5359
6206
  //#endregion
6207
+ //#region ../../packages/protocol/dist/forge-link.js
6208
+ /**
6209
+ * Linking a code platform (GitHub, GitLab) to a machine. The creator's device asks, with a signed
6210
+ * order, for the machine to be authorised at the platform; the machine does it itself, keeps the
6211
+ * token in its own system keychain and says what became of it in its report. The token is in none
6212
+ * of these shapes: a device only ever learns the state of a link (who, how far, until when) and,
6213
+ * while an authorisation is under way, the short code and the public address to enter it at.
6214
+ */
6215
+ /** The platforms a machine can be linked to. */
6216
+ const FORGE_PROVIDERS = ["github", "gitlab"];
6217
+ 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) => {
6218
+ const port = host.split(":")[1];
6219
+ return port === void 0 || Number(port) <= 65535;
6220
+ });
6221
+ z.strictObject({
6222
+ provider: z.enum(FORGE_PROVIDERS),
6223
+ host: ForgeHostSchema.optional()
6224
+ });
6225
+ const ForgeLinkStatusSchema = z.strictObject({
6226
+ provider: z.enum(FORGE_PROVIDERS),
6227
+ host: ForgeHostSchema,
6228
+ state: z.enum([
6229
+ "pending",
6230
+ "linked",
6231
+ "relink",
6232
+ "failed"
6233
+ ]),
6234
+ /** How the token was obtained: the platform's device flow, or given on the machine. */
6235
+ method: z.enum(["device", "token"]).optional(),
6236
+ /** The account the token belongs to (the platform's login). */
6237
+ account: z.string().min(1).max(100).optional(),
6238
+ access: z.enum([
6239
+ "read",
6240
+ "write",
6241
+ "unknown"
6242
+ ]).optional(),
6243
+ /** The scopes or permissions the platform reported, as it names them. */
6244
+ scopes: z.array(z.string().min(1).max(60)).max(12).optional(),
6245
+ repos: z.discriminatedUnion("kind", [z.strictObject({ kind: z.literal("all") }), z.strictObject({
6246
+ kind: z.literal("listed"),
6247
+ names: z.array(z.string().min(1).max(140)).max(30),
6248
+ more: z.boolean()
6249
+ })]).optional(),
6250
+ linked_at: z.iso.datetime().optional(),
6251
+ /** When the token stops working; absent when the platform did not say. */
6252
+ expires_at: z.iso.datetime().optional(),
6253
+ pending: z.strictObject({
6254
+ user_code: z.string().min(1).max(32),
6255
+ /** The public address to enter the code at. */
6256
+ verification_uri: z.string().max(300),
6257
+ expires_at: z.iso.datetime()
6258
+ }).optional(),
6259
+ failure: z.enum([
6260
+ "expired",
6261
+ "denied",
6262
+ "unreachable",
6263
+ "rejected",
6264
+ "not_saved"
6265
+ ]).optional()
6266
+ });
6267
+ const CODE_PATCH_MAX_CHARS = 4e4;
6268
+ const CODE_TEXT_MAX_CHARS = 2e3;
6269
+ /** The number of a pull request (GitHub) or merge request (GitLab) in its repository. */
6270
+ const CodePullNumberSchema = z.int().min(1).max(1e9);
6271
+ const CODE_PULL_STATES = [
6272
+ "open",
6273
+ "draft",
6274
+ "merged",
6275
+ "closed"
6276
+ ];
6277
+ /** Where the checks of a pull request stand; `unknown` when the bridge did not read them. */
6278
+ const CODE_CHECKS_STATES = [
6279
+ "passed",
6280
+ "failed",
6281
+ "running",
6282
+ "none",
6283
+ "unknown"
6284
+ ];
6285
+ /** Where the review of a pull request stands; `unknown` when the bridge did not read it. */
6286
+ const CODE_REVIEW_STATES = [
6287
+ "approved",
6288
+ "changes_requested",
6289
+ "pending",
6290
+ "none",
6291
+ "unknown"
6292
+ ];
6293
+ /** The repository the pull requests are read from, as the machine knows it. */
6294
+ const CodeRepositorySchema = z.strictObject({
6295
+ provider: z.enum(FORGE_PROVIDERS),
6296
+ host: ForgeHostSchema,
6297
+ /** `owner/name` (GitLab: the group path, possibly nested). */
6298
+ path: z.string().min(3).max(200)
6299
+ });
6300
+ const CodeChecksSummarySchema = z.strictObject({
6301
+ state: z.enum(CODE_CHECKS_STATES),
6302
+ passed: z.int().min(0).max(1e4),
6303
+ failed: z.int().min(0).max(1e4),
6304
+ total: z.int().min(0).max(1e4)
6305
+ });
6306
+ const CodePullSchema = z.strictObject({
6307
+ number: CodePullNumberSchema,
6308
+ title: z.string().max(200),
6309
+ state: z.enum(CODE_PULL_STATES),
6310
+ /** The platform's login of the author. */
6311
+ author: z.string().min(1).max(100).optional(),
6312
+ /** The branch the work is on, and the branch it aims at. */
6313
+ branch: z.string().max(200),
6314
+ base: z.string().max(200),
6315
+ /** The address of the pull request at the platform, built by the bridge. */
6316
+ url: z.string().max(400),
6317
+ created_at: z.iso.datetime(),
6318
+ updated_at: z.iso.datetime(),
6319
+ checks: CodeChecksSummarySchema,
6320
+ review: z.enum(CODE_REVIEW_STATES),
6321
+ additions: z.int().min(0).optional(),
6322
+ deletions: z.int().min(0).optional(),
6323
+ files: z.int().min(0).optional()
6324
+ });
6325
+ const CodeCheckSchema = z.strictObject({
6326
+ name: z.string().min(1).max(120),
6327
+ state: z.enum([
6328
+ "passed",
6329
+ "failed",
6330
+ "running",
6331
+ "queued",
6332
+ "skipped"
6333
+ ]),
6334
+ /** How long the step took, in seconds, when the platform says. */
6335
+ seconds: z.int().min(0).max(1e6).optional()
6336
+ });
6337
+ const CodeReviewSchema = z.strictObject({
6338
+ author: z.string().min(1).max(100).optional(),
6339
+ verdict: z.enum([
6340
+ "approved",
6341
+ "changes_requested",
6342
+ "commented",
6343
+ "dismissed"
6344
+ ]),
6345
+ body: z.string().max(CODE_TEXT_MAX_CHARS).optional(),
6346
+ at: z.iso.datetime().optional()
6347
+ });
6348
+ /** A remark anchored to a line of a file of the pull request. */
6349
+ const CodeRemarkSchema = z.strictObject({
6350
+ id: z.string().min(1).max(40),
6351
+ author: z.string().min(1).max(100).optional(),
6352
+ path: z.string().min(1).max(1024),
6353
+ /** The line of the file: after the change (`new`) or before it (`old`, a removed line). */
6354
+ line: z.int().min(1).max(1e7),
6355
+ side: z.enum(["new", "old"]),
6356
+ body: z.string().max(CODE_TEXT_MAX_CHARS),
6357
+ at: z.iso.datetime().optional()
6358
+ });
6359
+ /**
6360
+ * One file of a difference. `patch` is the platform's unified difference without its header (it
6361
+ * starts at the first `@@` line), each line that looked like a secret replaced by the fixed
6362
+ * notice; absent for a binary or a very large file.
6363
+ */
6364
+ const CodeDiffFileSchema = z.strictObject({
6365
+ path: z.string().min(1).max(1024),
6366
+ from: z.string().min(1).max(1024).optional(),
6367
+ status: z.enum([
6368
+ "added",
6369
+ "modified",
6370
+ "deleted",
6371
+ "renamed"
6372
+ ]),
6373
+ additions: z.int().min(0),
6374
+ deletions: z.int().min(0),
6375
+ patch: z.string().max(CODE_PATCH_MAX_CHARS).optional(),
6376
+ /** The patch is cut at the longest the bridge sends. */
6377
+ truncated: z.literal(true).optional(),
6378
+ /** The platform shows no text for this file (binary, or too large for it). */
6379
+ unavailable: z.literal(true).optional(),
6380
+ /** The file is a secret's by its name: its difference is never sent. */
6381
+ protected: z.literal(true).optional()
6382
+ });
6383
+ /** The editors a link can open. The scheme of each is fixed on the device; nothing is run. */
6384
+ const CODE_EDITORS = [
6385
+ "vscode",
6386
+ "cursor",
6387
+ "windsurf",
6388
+ "vscodium",
6389
+ "zed",
6390
+ "idea",
6391
+ "webstorm",
6392
+ "pycharm"
6393
+ ];
6394
+ const CODE_PLATFORMS = [
6395
+ "windows",
6396
+ "macos",
6397
+ "linux",
6398
+ "other"
6399
+ ];
6400
+ /** The working copy an editor is opened on: the product's own folder, or a story's branch. */
6401
+ const CodeEditorRefSchema = z.discriminatedUnion("kind", [z.strictObject({ kind: z.literal("worktree") }), z.strictObject({
6402
+ kind: z.literal("workspace"),
6403
+ branch: z.string().min(1).max(200)
6404
+ })]);
6405
+ /** The request shapes (without the identifier, the version and the product, which the file reading adds). */
6406
+ const CodePullsRequestFields = { op: z.literal("pulls") };
6407
+ const CodePullRequestFields = {
6408
+ op: z.literal("pull"),
6409
+ number: CodePullNumberSchema
6410
+ };
6411
+ const CodeDiffRequestFields = {
6412
+ op: z.literal("pull_diff"),
6413
+ number: CodePullNumberSchema
6414
+ };
6415
+ const CodeEditorRequestFields = {
6416
+ op: z.literal("editor"),
6417
+ ref: CodeEditorRefSchema
6418
+ };
6419
+ /** What an answer carries. */
6420
+ const CodePullsDataSchema = z.strictObject({
6421
+ op: z.literal("pulls"),
6422
+ repository: CodeRepositorySchema,
6423
+ pulls: z.array(CodePullSchema).max(30),
6424
+ /** True when the platform has more than the bridge lists. */
6425
+ truncated: z.boolean()
6426
+ });
6427
+ const CodePullDataSchema = z.strictObject({
6428
+ op: z.literal("pull"),
6429
+ repository: CodeRepositorySchema,
6430
+ pull: CodePullSchema,
6431
+ checks: z.array(CodeCheckSchema).max(60),
6432
+ reviews: z.array(CodeReviewSchema).max(30),
6433
+ remarks: z.array(CodeRemarkSchema).max(100)
6434
+ });
6435
+ const CodeDiffDataSchema = z.strictObject({
6436
+ op: z.literal("pull_diff"),
6437
+ repository: CodeRepositorySchema,
6438
+ number: CodePullNumberSchema,
6439
+ files: z.array(CodeDiffFileSchema).max(100),
6440
+ /** True when the pull request changes more files than the bridge sends. */
6441
+ truncated: z.boolean()
6442
+ });
6443
+ const CodeEditorDataSchema = z.strictObject({
6444
+ op: z.literal("editor"),
6445
+ /** The folder of the working copy on the machine. */
6446
+ folder: z.string().min(1).max(1024),
6447
+ /** The branch checked out there, when it is on one. */
6448
+ branch: z.string().min(1).max(200).optional(),
6449
+ /** The system of the machine, so that a device of another system knows the link is not for it. */
6450
+ platform: z.enum(CODE_PLATFORMS),
6451
+ /** The editors found on the machine, best known first. */
6452
+ detected: z.array(z.enum(CODE_EDITORS)).max(CODE_EDITORS.length)
6453
+ });
6454
+ /** The stable codes of a refusal that only the code reading gives. */
6455
+ const CODE_ERROR_CODES = [
6456
+ "NOT_LINKED",
6457
+ "NO_REMOTE",
6458
+ "FORGE_REFUSED",
6459
+ "FORGE_UNREACHABLE",
6460
+ "FORGE_BUSY"
6461
+ ];
6462
+ /** The request shape (without the identifier, the version and the product, which the file reading adds). */
6463
+ const CostsRequestFields = {
6464
+ op: z.literal("costs"),
6465
+ period: z.enum([
6466
+ "today",
6467
+ "week",
6468
+ "sprint",
6469
+ "month",
6470
+ "all"
6471
+ ]).optional()
6472
+ };
6473
+ const Count$1 = z.int().min(0).max(Number.MAX_SAFE_INTEGER);
6474
+ /** What a set of measured parts consumed, and what it costs at the table's prices. */
6475
+ const CostFiguresSchema = z.strictObject({
6476
+ /** The dollars of the models the table prices; absent when `nature` is `unknown`. */
6477
+ usd: z.number().min(0).max(1e8).optional(),
6478
+ nature: z.string().min(1).max(16),
6479
+ tokens_in: Count$1,
6480
+ tokens_out: Count$1,
6481
+ cache_read: Count$1,
6482
+ cache_write: Count$1,
6483
+ /** Working time, in milliseconds. */
6484
+ active_ms: Count$1,
6485
+ /** The models that have no price in the table (their tokens count, their dollars do not). */
6486
+ unpriced: z.array(z.string().min(1).max(100)).max(8)
6487
+ });
6488
+ const CostWorkSchema = z.strictObject({
6489
+ kind: z.string().min(1).max(24),
6490
+ /** The file's name, the checkpoint, the story, ... that the work is about. */
6491
+ key: z.string().min(1).max(128),
6492
+ label: z.string().max(200).optional(),
6493
+ /** The agent that did it; `noah` for the creator's conversation with Noah. */
6494
+ agent: z.string().min(1).max(64),
6495
+ story: z.string().regex(/^[1-9]\d{0,5}-[1-9]\d{0,5}$/).optional(),
6496
+ figures: CostFiguresSchema
6497
+ });
6498
+ const CostPhaseSchema = z.strictObject({
6499
+ /** The phase of the method; absent for what ran when no phase was current. */
6500
+ phase: z.string().min(1).max(32).optional(),
6501
+ figures: CostFiguresSchema,
6502
+ works: z.array(CostWorkSchema).max(60),
6503
+ /** True when the phase holds more works than listed (the figures count them all). */
6504
+ truncated: z.boolean()
6505
+ });
6506
+ const CostAgentSchema = z.strictObject({
6507
+ agent: z.string().min(1).max(64),
6508
+ figures: CostFiguresSchema
6509
+ });
6510
+ const CostStorySchema = z.strictObject({
6511
+ story: z.string().regex(/^[1-9]\d{0,5}-[1-9]\d{0,5}$/),
6512
+ /** The sprint that carries it, when one does. */
6513
+ sprint: z.string().min(1).max(64).optional(),
6514
+ figures: CostFiguresSchema
6515
+ });
6516
+ const CostSprintSchema = z.strictObject({
6517
+ id: z.string().min(1).max(64),
6518
+ goal: z.string().max(200),
6519
+ /** Where the sprint stands (`in_force`, `superseded`, ...); a device shows an unknown one in neutral words. */
6520
+ status: z.string().min(1).max(24),
6521
+ stories: Count$1,
6522
+ /** The budget the sprint was planned with, in dollars. */
6523
+ budget_usd: z.number().min(0).max(1e8),
6524
+ figures: CostFiguresSchema
6525
+ });
6526
+ /** What the agents declared on their own, apart from the measure. */
6527
+ const CostDeclaredAgentSchema = z.strictObject({
6528
+ agent: z.string().min(1).max(64),
6529
+ /** Dollars declared as real, and as estimated. */
6530
+ usd: z.number().min(0).max(1e8),
6531
+ usd_estimated: z.number().min(0).max(1e8),
6532
+ tokens_in: Count$1,
6533
+ tokens_out: Count$1
6534
+ });
6535
+ const CostDeclaredSchema = z.strictObject({
6536
+ usd: z.number().min(0).max(1e8),
6537
+ usd_estimated: z.number().min(0).max(1e8),
6538
+ tokens_in: Count$1,
6539
+ tokens_out: Count$1,
6540
+ agents: z.array(CostDeclaredAgentSchema).max(60)
6541
+ });
6542
+ const CostsDataSchema = z.strictObject({
6543
+ op: z.literal("costs"),
6544
+ period: z.string().min(1).max(16),
6545
+ /** The price table the dollars were computed with. */
6546
+ table: z.strictObject({
6547
+ version: z.string().min(1).max(32),
6548
+ as_of: z.string().min(1).max(32)
6549
+ }),
6550
+ /** False when no session was measured in the period: no figure, never a zero. */
6551
+ measured: z.boolean(),
6552
+ /** How many measured sessions the figures come from. */
6553
+ sessions: Count$1,
6554
+ total: CostFiguresSchema,
6555
+ phases: z.array(CostPhaseSchema).max(12),
6556
+ agents: z.array(CostAgentSchema).max(60),
6557
+ stories: z.array(CostStorySchema).max(400),
6558
+ sprints: z.array(CostSprintSchema).max(60),
6559
+ /** Absent when no agent declared a cost (or the period is not `all`: declarations carry no date). */
6560
+ declared: CostDeclaredSchema.optional(),
6561
+ /** True when a list was cut at its limit (the totals count everything). */
6562
+ truncated: z.boolean()
6563
+ });
6564
+ //#endregion
5360
6565
  //#region ../../packages/protocol/dist/install.js
5361
6566
  /**
5362
6567
  * The installations the approved architecture plans. The architecture document
@@ -6010,216 +7215,303 @@ const AgentDelegatedPayloadSchema = z.strictObject({
6010
7215
  brief: BriefSchema
6011
7216
  });
6012
7217
  //#endregion
6013
- //#region ../../packages/protocol/dist/device-trust.js
7218
+ //#region ../../packages/protocol/dist/trace.js
6014
7219
  /**
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.
7220
+ * Traceability and deviations. Each requirement of the PRD (`EF-n`, `ENF-n`) is linked,
7221
+ * at read time, to the stories that cite it, their pull requests, the tests that cite it and the
7222
+ * deviations recorded on it: nothing of that link is ever written in a document. A deviation is a
7223
+ * signed event of the journal: an agent (the QA reviewer) found the delivered work drifting from
7224
+ * requirements a started story cites. Its id, like an item of deferred work's, is `V-` then the
7225
+ * last 10 characters of the ULID of its event, never written in the `payload`, never reused.
6023
7226
  */
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"
7227
+ /** Event types of the deviations, each at version 1. */
7228
+ const DEVIATION_EVENT_TYPES = {
7229
+ reported: "deviation.reported",
7230
+ resolved: "deviation.resolved"
6031
7231
  };
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"
7232
+ /** Where a requirement stands: to do, in development, in review, delivered, or deviating. */
7233
+ const REQUIREMENT_STATUSES = [
7234
+ "todo",
7235
+ "in_development",
7236
+ "in_review",
7237
+ "delivered",
7238
+ "deviation"
6049
7239
  ];
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);
7240
+ /** The requirements traced: the functional and non-functional ones of the PRD. */
7241
+ const TRACED_REQUIREMENT_PATTERN = /^(?:EF|ENF)-[1-9]\d*$/;
7242
+ /** A traced requirement as typed (`ef-18`, with spaces): trimmed, in upper case, then checked. */
7243
+ const TracedRequirementInputSchema = z.string().trim().toUpperCase().pipe(z.string().regex(TRACED_REQUIREMENT_PATTERN, { message: "expected EF-n or ENF-n, for example EF-18" }));
7244
+ /** `V-` then the last 10 characters of the ULID of the event that reported the deviation. */
7245
+ const DeviationIdSchema = z.string().regex(/^V-[0-9A-HJKMNP-TV-Z]{10}$/, { message: "expected V- then 10 characters, for example V-7M2K9QX4TB" });
7246
+ /** An id as typed (`v-7m2k9qx4tb`, with spaces): trimmed and in upper case, then checked. */
7247
+ const DeviationIdInputSchema = z.string().trim().toUpperCase().pipe(DeviationIdSchema);
7248
+ /** The id of the deviation reported by the event `eventId`: stable, never renumbered. */
7249
+ function deviationIdOf(eventId) {
7250
+ return `V-${eventId.slice(-10)}`;
6098
7251
  }
7252
+ const NO_CONTROL$2 = { message: "expected no control character" };
7253
+ 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$2);
7254
+ const DetailSchema = z.string().trim().min(1).max(2e3).refine((detail) => !hasControlCharacter(detail, true), NO_CONTROL$2);
7255
+ const ReasonSchema = z.string().trim().min(1).max(1e3).refine((reason) => !hasControlCharacter(reason, true), NO_CONTROL$2);
7256
+ const RequirementIdSchema = z.string().regex(REQUIREMENT_ID_PATTERN, { message: "expected a requirement id (EF-n, ENF-n, PU-n, MS-n or AD-n)" });
7257
+ const requirementsOf = (id) => z.array(id).min(1).max(20).refine((ids) => new Set(ids).size === ids.length, { message: "requirements must differ" });
6099
7258
  /**
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).
7259
+ * `payload` of `deviation.reported`. The story is the envelope's (required); `pr` is the number of
7260
+ * the story's pull request when the deviation was reported, set by the engine from the journal.
6112
7261
  */
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
7262
+ const DeviationReportedPayloadSchema = z.strictObject({
7263
+ requirements: requirementsOf(RequirementIdSchema),
7264
+ summary: SummarySchema,
7265
+ detail: DetailSchema.optional(),
7266
+ pr: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER).optional(),
7267
+ /** The commit compared with the requirements. */
7268
+ commit: CommitIdSchema.optional()
6172
7269
  });
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
7270
+ /** `payload` of `deviation.resolved`: the deviation, and how it was fixed or why it is lifted. */
7271
+ const DeviationResolvedPayloadSchema = z.strictObject({
7272
+ deviation: DeviationIdSchema,
7273
+ reason: ReasonSchema
7274
+ });
7275
+ /** A requirement id as typed: in upper case, then checked. */
7276
+ const RequirementInputSchema = z.string().trim().toUpperCase().pipe(RequirementIdSchema);
7277
+ /** Input of `report_deviation`: the story brought to its canonical form. */
7278
+ const ReportDeviationInputSchema = z.strictObject({
7279
+ agent: AgentIdSchema.max(64),
7280
+ story: StoryIdSchema.overwrite(canonicalStoryId),
7281
+ requirements: requirementsOf(RequirementInputSchema),
7282
+ summary: SummarySchema,
7283
+ detail: DetailSchema.optional(),
7284
+ commit: CommitIdSchema.optional()
7285
+ });
7286
+ /** Input of `lift_deviation`: the agent that reported it lifts it, with why. */
7287
+ const ResolveDeviationInputSchema = z.strictObject({
7288
+ agent: AgentIdSchema.max(64),
7289
+ deviation: DeviationIdInputSchema,
7290
+ reason: ReasonSchema
7291
+ });
7292
+ /** The reason the creator gives at the terminal (`trace resolve`). */
7293
+ const DeviationReasonSchema = ReasonSchema;
7294
+ /** Input of `get_trace`: one requirement, or every one when absent. */
7295
+ const GetTraceInputSchema = z.strictObject({ requirement: z.string().max(32).optional() });
7296
+ const DOCS_TEXT_MAX_CHARS = 2e3;
7297
+ /** Where a story stands, as the engine sees it. */
7298
+ const DOCS_STORY_STATUSES = [
7299
+ "todo",
7300
+ "in_progress",
7301
+ "in_review",
7302
+ "blocked",
7303
+ "done"
7304
+ ];
7305
+ const StoryRef = z.string().regex(/^[1-9]\d{0,5}-[1-9]\d{0,5}$/);
7306
+ const RequirementRef = z.string().regex(TRACED_REQUIREMENT_PATTERN).max(16);
7307
+ /** The request shapes (without the identifier, the version and the product, which the file reading adds). */
7308
+ const DocsListRequestFields = { op: z.literal("docs") };
7309
+ const DocsTraceRequestFields = {
7310
+ op: z.literal("trace"),
7311
+ requirement: RequirementRef
7312
+ };
7313
+ const DocsRetrosRequestFields = { op: z.literal("retros") };
7314
+ /** One document of the product: a file of `docs/` or `epics/` (the stories are told by the breakdown). */
7315
+ const DocsDocumentSchema = z.strictObject({
7316
+ /** The path from the product's folder, with `/`. */
7317
+ path: z.string().min(1).max(300),
7318
+ /** The header's kind (`prd`, `architecture`...), or `unknown` when the header is not valid. */
7319
+ kind: z.string().min(1).max(32),
7320
+ id: z.string().min(1).max(64).optional(),
7321
+ title: z.string().min(1).max(200).optional(),
7322
+ /** The agent that owns it, or `creator`. */
7323
+ owner: z.string().min(1).max(64).optional(),
7324
+ standing: z.string().min(1).max(32),
7325
+ /** The requirements it defines (active and retired), when it defines any. */
7326
+ requirements: z.int().min(0).max(1e5).optional(),
7327
+ /** The documents and requirements its header links to. */
7328
+ links: z.array(z.string().min(1).max(64)).max(40).optional()
7329
+ });
7330
+ const DocsRequirementSchema = z.strictObject({
7331
+ id: RequirementRef,
7332
+ text: z.string().max(400),
7333
+ /** The document that defines it. */
7334
+ path: z.string().min(1).max(300),
7335
+ /** Retired: the id stays reserved, the status below is not meaningful. */
7336
+ retired: z.boolean(),
7337
+ status: z.enum(REQUIREMENT_STATUSES),
7338
+ stories: z.array(StoryRef).max(100),
7339
+ /** The test files that cite it, or null when the tests could not be read. */
7340
+ tests: z.int().min(0).max(1e5).nullable(),
7341
+ pull_requests: z.int().min(0).max(1e5),
7342
+ /** Its open deviations. */
7343
+ deviations: z.int().min(0).max(1e5)
7344
+ });
7345
+ const DocsDeviationSchema = z.strictObject({
7346
+ id: z.string().regex(/^V-[0-9A-HJKMNP-TV-Z]{10}$/),
7347
+ story: StoryRef,
7348
+ requirements: z.array(z.string().min(1).max(16)).max(20),
7349
+ summary: z.string().max(200),
7350
+ reported_by: z.string().min(1).max(64),
7351
+ reported_at: z.string().max(40)
7352
+ });
7353
+ const DocsBreakdownStorySchema = z.strictObject({
7354
+ id: StoryRef,
7355
+ title: z.string().min(1).max(200).optional(),
7356
+ status: z.enum(DOCS_STORY_STATUSES),
7357
+ depends_on: z.array(StoryRef).max(40),
7358
+ requirements: z.array(RequirementRef).max(40)
7359
+ });
7360
+ const DocsEpicSchema = z.strictObject({
7361
+ /** `epic-2`. */
7362
+ id: z.string().regex(/^epic-[1-9]\d{0,5}$/),
7363
+ title: z.string().min(1).max(200).optional(),
7364
+ /** The path of the epic's document, when it has one. */
7365
+ path: z.string().min(1).max(300).optional(),
7366
+ out_of_scope: z.array(RequirementRef).max(200),
7367
+ stories: z.array(DocsBreakdownStorySchema).max(600)
7368
+ });
7369
+ const DocsCoverageSchema = z.strictObject({
7370
+ /** The active functional and non-functional requirements. */
7371
+ total: z.int().min(0).max(1e5),
7372
+ /** Those a story carries. */
7373
+ covered: z.int().min(0).max(1e5),
7374
+ /** Those an epic leaves aside. */
7375
+ out_of_scope: z.int().min(0).max(1e5),
7376
+ /** The others, by id. */
7377
+ uncovered: z.array(RequirementRef).max(200)
7378
+ });
7379
+ const DOCS_TESTS_STATES = [
7380
+ "read",
7381
+ "truncated",
7382
+ "unavailable"
7383
+ ];
7384
+ const CountsSchema = z.strictObject({
7385
+ todo: z.int().min(0),
7386
+ in_development: z.int().min(0),
7387
+ in_review: z.int().min(0),
7388
+ delivered: z.int().min(0),
7389
+ deviation: z.int().min(0)
7390
+ });
7391
+ const DocsListDataSchema = z.strictObject({
7392
+ op: z.literal("docs"),
7393
+ documents: z.array(DocsDocumentSchema).max(200),
7394
+ /** True when the product holds more documents than the bridge lists. */
7395
+ truncated: z.boolean(),
7396
+ requirements: z.array(DocsRequirementSchema).max(800),
7397
+ /** How many active requirements stand at each status. */
7398
+ counts: CountsSchema,
7399
+ /** How the tests were read: all of them, a part, or not at all. */
7400
+ tests: z.enum(DOCS_TESTS_STATES),
7401
+ /** The open deviations. */
7402
+ deviations: z.array(DocsDeviationSchema).max(50),
7403
+ epics: z.array(DocsEpicSchema).max(100),
7404
+ /** Absent while no story exists: nothing is covered or not yet. */
7405
+ coverage: DocsCoverageSchema.optional()
7406
+ });
7407
+ const DocsTraceDeviationSchema = DocsDeviationSchema.extend({
7408
+ detail: z.string().max(DOCS_TEXT_MAX_CHARS).optional(),
7409
+ status: z.enum(["open", "resolved"]),
7410
+ resolved: z.strictObject({
7411
+ reason: z.string().max(DOCS_TEXT_MAX_CHARS),
7412
+ by: z.string().min(1).max(64),
7413
+ at: z.string().max(40)
7414
+ }).optional()
7415
+ });
7416
+ const DocsTraceDataSchema = z.strictObject({
7417
+ op: z.literal("trace"),
7418
+ requirement: z.strictObject({
7419
+ id: RequirementRef,
7420
+ text: z.string().max(400),
7421
+ path: z.string().min(1).max(300),
7422
+ line: z.int().min(1).max(1e7),
7423
+ retired: z.boolean(),
7424
+ status: z.enum(REQUIREMENT_STATUSES),
7425
+ stories: z.array(z.strictObject({
7426
+ id: StoryRef,
7427
+ title: z.string().min(1).max(200).optional(),
7428
+ status: z.enum(DOCS_STORY_STATUSES)
7429
+ })).max(100),
7430
+ tests: z.array(z.strictObject({
7431
+ path: z.string().min(1).max(300),
7432
+ /** 0: the file's name cites it. */
7433
+ line: z.int().min(0).max(1e7)
7434
+ })).max(100),
7435
+ pull_requests: z.array(z.strictObject({
7436
+ story: StoryRef,
7437
+ number: z.int().min(1).max(1e9)
7438
+ })).max(100),
7439
+ deviations: z.array(DocsTraceDeviationSchema).max(100)
7440
+ }),
7441
+ tests_state: z.enum(DOCS_TESTS_STATES)
7442
+ });
7443
+ /** Where a retrospective stands: the creator's answer to its checkpoint decides. */
7444
+ const RETRO_STATUSES = [
7445
+ "open",
7446
+ "approved",
7447
+ "changes_requested",
7448
+ "dismissed"
7449
+ ];
7450
+ /**
7451
+ * What became of a proposed lesson: still `proposed` (nothing recorded), `active` (recorded and in
7452
+ * force), `retired` or `superseded` since, or `not_kept` (its retrospective was not approved).
7453
+ */
7454
+ const RETRO_LESSON_STATUSES = [
7455
+ "proposed",
7456
+ "active",
7457
+ "retired",
7458
+ "superseded",
7459
+ "not_kept"
7460
+ ];
7461
+ const LessonText = z.string().max(DOCS_TEXT_MAX_CHARS);
7462
+ const LessonRef = z.string().regex(/^L-[0-9A-HJKMNP-TV-Z]{10}-[1-9][0-9]?$/);
7463
+ const DocsRetroSchema = z.strictObject({
7464
+ id: z.string().regex(/^R-[0-9A-HJKMNP-TV-Z]{10}$/),
7465
+ scope: z.strictObject({
7466
+ kind: z.enum(["epic", "sprint"]),
7467
+ id: z.string().min(1).max(16)
7468
+ }),
7469
+ at: z.string().max(40),
7470
+ proposed_by: z.string().min(1).max(64),
7471
+ status: z.enum(RETRO_STATUSES),
7472
+ summary: LessonText,
7473
+ lessons: z.array(z.strictObject({
7474
+ id: LessonRef,
7475
+ title: z.string().max(200),
7476
+ text: LessonText,
7477
+ tags: z.array(z.string().min(1).max(32)).max(5),
7478
+ agents: z.array(z.string().min(1).max(64)).max(10),
7479
+ status: z.enum(RETRO_LESSON_STATUSES)
7480
+ })).max(12),
7481
+ retire: z.array(z.strictObject({
7482
+ lesson: LessonRef,
7483
+ reason: LessonText
7484
+ })).max(12),
7485
+ actions: z.array(z.strictObject({
7486
+ id: z.string().min(1).max(32),
7487
+ text: LessonText,
7488
+ owner: z.string().min(1).max(64).optional()
7489
+ })).max(12)
7490
+ });
7491
+ /** A lesson in force: what the agents read at the start of a task. */
7492
+ const DocsMemoryLessonSchema = z.strictObject({
7493
+ id: LessonRef,
7494
+ title: z.string().max(200),
7495
+ text: LessonText,
7496
+ tags: z.array(z.string().min(1).max(32)).max(5),
7497
+ agents: z.array(z.string().min(1).max(64)).max(10),
7498
+ at: z.string().max(40),
7499
+ /** The retrospective it comes from. */
7500
+ retro: z.string().regex(/^R-[0-9A-HJKMNP-TV-Z]{10}$/),
7501
+ /** True when it was also promoted to the common memory of the machine. */
7502
+ promoted: z.boolean()
7503
+ });
7504
+ const DocsRetrosDataSchema = z.strictObject({
7505
+ op: z.literal("retros"),
7506
+ retrospectives: z.array(DocsRetroSchema).max(40),
7507
+ /** The lessons in force, newest first. */
7508
+ memory: z.array(DocsMemoryLessonSchema).max(120),
7509
+ /**
7510
+ * The retrospectives whose content this machine could not read (the product's key is missing
7511
+ * here): they are counted, never guessed.
7512
+ */
7513
+ unreadable: z.int().min(0).max(1e5)
6177
7514
  });
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
7515
  //#endregion
6224
7516
  //#region ../../packages/protocol/dist/elicitation.js
6225
7517
  /**
@@ -6346,14 +7638,18 @@ const DEFAULT_MERGE_METHOD = "merge";
6346
7638
  * 3.28: `forge`, a merge the creator did on the forge itself (the pull request merged by hand),
6347
7639
  * which the engine recorded from what the forge shows: no approval to wait for, the forge's own
6348
7640
  * 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.
7641
+ * agent responsible decided because the creator did not keep the merges for themselves. `web`, the
7642
+ * merge the machine did once the signed order of a browser the creator trusts was carried out, in
7643
+ * the same write as that answer; `mobile`, the same for the signed order of a phone.
6350
7644
  */
6351
7645
  const MERGE_SOURCES = [
6352
7646
  "chat",
6353
7647
  "elicitation",
6354
7648
  "terminal",
6355
7649
  "forge",
6356
- "agent"
7650
+ "agent",
7651
+ "web",
7652
+ "mobile"
6357
7653
  ];
6358
7654
  /** Most changed files one merge records (the knowledge base reads them). */
6359
7655
  const MAX_MERGED_FILES = 1e3;
@@ -6766,6 +8062,273 @@ const LoopInputSchema = z.strictObject({
6766
8062
  message: "a story goes with an approach (note) or a result (seen), and only with them",
6767
8063
  path: ["story"]
6768
8064
  });
8065
+ const ProductStageSchema = z.enum([
8066
+ "framing",
8067
+ "development",
8068
+ "launch",
8069
+ "online"
8070
+ ]);
8071
+ /** How a product starts: from an idea (no repository), from a repository already on the machine, or from an empty repository. */
8072
+ const PRODUCT_STARTS = [
8073
+ "idea",
8074
+ "existing",
8075
+ "empty"
8076
+ ];
8077
+ /** What a machine says of the repository of a product: none, one it made empty, or one that was already there. */
8078
+ const PRODUCT_REPOSITORIES = [
8079
+ "none",
8080
+ "empty",
8081
+ "existing"
8082
+ ];
8083
+ const PRODUCT_IDEA_MAX = 4e3;
8084
+ /** The longest folder path a machine tells of a product (the full path on the machine). */
8085
+ const PRODUCT_FOLDER_PATH_MAX = 1e3;
8086
+ /** The identifier of a place a machine allows products in (`home`, `place-2`): the machine names it, the device only repeats it. */
8087
+ const PRODUCT_LOCATION_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
8088
+ const hasControl = (value) => [...value].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127);
8089
+ /** A name the creator gives a product: trimmed, one line, no control character. */
8090
+ function isProductName(value) {
8091
+ return value.length >= 1 && value.length <= 80 && value === value.trim() && !hasControl(value);
8092
+ }
8093
+ /**
8094
+ * A folder name inside a place the machine allows: one segment, never a path. It holds no
8095
+ * separator, no drive or stream mark, no control character, is not `.` or `..`, and does not end
8096
+ * with a dot or a space (which a system may drop silently).
8097
+ */
8098
+ function isFolderName(value) {
8099
+ return value.length >= 1 && value.length <= 100 && value !== "." && value !== ".." && !/[\\/:*?"<>|]/.test(value) && !hasControl(value) && !/[. ]$/.test(value);
8100
+ }
8101
+ /**
8102
+ * A step of the way into a place: one folder name, never a path, and never a hidden one (a name
8103
+ * that starts with a dot). The machine resolves the steps itself and refuses what leaves the place.
8104
+ */
8105
+ function isBrowseStep(value) {
8106
+ return isFolderName(value) && !value.startsWith(".");
8107
+ }
8108
+ z.strictObject({
8109
+ name: z.string().refine(isProductName, { message: "a name of one line, trimmed" }),
8110
+ start: z.enum(PRODUCT_STARTS),
8111
+ location: z.string().regex(PRODUCT_LOCATION_ID_PATTERN),
8112
+ folder: z.string().refine(isFolderName, { message: "a folder name, not a path" }).optional(),
8113
+ /** The folders to go through inside the place, one name each (as the machine listed them), to make the product there. */
8114
+ path: z.array(z.string().refine(isBrowseStep, { message: "a folder name, not a path" })).max(12).optional(),
8115
+ preset: z.enum(PROFILE_PRESET_IDS),
8116
+ stage: ProductStageSchema,
8117
+ idea: z.string().min(1).max(PRODUCT_IDEA_MAX).optional()
8118
+ }).superRefine((params, context) => {
8119
+ if (params.start === "existing" && params.folder === void 0) context.addIssue({
8120
+ code: "custom",
8121
+ path: ["folder"],
8122
+ message: "a repository to take"
8123
+ });
8124
+ if (params.start === "existing" && params.path !== void 0) context.addIssue({
8125
+ code: "custom",
8126
+ path: ["path"],
8127
+ message: "not for an existing one"
8128
+ });
8129
+ if (params.start !== "existing" && params.folder !== void 0) context.addIssue({
8130
+ code: "custom",
8131
+ path: ["folder"],
8132
+ message: "only for an existing one"
8133
+ });
8134
+ if (params.start !== "idea" && params.idea !== void 0) context.addIssue({
8135
+ code: "custom",
8136
+ path: ["idea"],
8137
+ message: "only with an idea"
8138
+ });
8139
+ });
8140
+ z.strictObject({});
8141
+ z.strictObject({
8142
+ stage: ProductStageSchema.optional(),
8143
+ paused: z.boolean().optional(),
8144
+ archived: z.boolean().optional()
8145
+ }).refine((params) => Object.keys(params).length > 0, { message: "at least one mark" });
8146
+ z.strictObject({ profile: ProfileSchema });
8147
+ /** What a machine says of the place it allows products in. */
8148
+ const ProductLocationSchema = z.strictObject({
8149
+ id: z.string().regex(PRODUCT_LOCATION_ID_PATTERN),
8150
+ /** The folder as the creator reads it (the home folder written `~`). */
8151
+ label: z.string().min(1).max(200),
8152
+ /** The folders of this place that hold a repository and no product yet: ones to start from. */
8153
+ folders: z.array(z.string().refine(isFolderName)).max(50),
8154
+ /** True for the place the creator chose the last time (the one to propose first). */
8155
+ last: z.literal(true).optional()
8156
+ });
8157
+ /**
8158
+ * A creation the machine is making, has made or could not make. It is told by its ticket (the
8159
+ * reference the order answered with); a finished one names the product it made. A finished or
8160
+ * failed one stays a few minutes in the report, so that a device that was away still learns it.
8161
+ */
8162
+ const ProductCreationSchema = z.strictObject({
8163
+ id: UlidSchema,
8164
+ name: z.string().min(1).max(80),
8165
+ state: z.enum([
8166
+ "running",
8167
+ "done",
8168
+ "failed"
8169
+ ]),
8170
+ /** The product made, once `done`. */
8171
+ product: UlidSchema.optional(),
8172
+ /** Why it failed. */
8173
+ code: z.enum([
8174
+ "folder",
8175
+ "repository",
8176
+ "engine",
8177
+ "link"
8178
+ ]).optional()
8179
+ });
8180
+ /** The marks and the profile a machine reports for a product it serves. */
8181
+ const ProductLifeSchema = z.strictObject({
8182
+ stage: ProductStageSchema.optional(),
8183
+ paused: z.literal(true).optional(),
8184
+ archived: z.literal(true).optional(),
8185
+ repository: z.enum(PRODUCT_REPOSITORIES).optional(),
8186
+ /** The full path of the product's folder on this machine: told to the creator's own devices only, never published. */
8187
+ folder: z.string().min(1).max(PRODUCT_FOLDER_PATH_MAX).optional(),
8188
+ profile: ProfileSchema.optional(),
8189
+ autonomy: ProductAutonomySchema.optional()
8190
+ });
8191
+ //#endregion
8192
+ //#region ../../packages/protocol/dist/tolerant-reader.js
8193
+ const cache = /* @__PURE__ */ new WeakMap();
8194
+ const defOf = (schema) => schema._zod.def;
8195
+ const cloneWith = (schema, def) => schema.clone(def);
8196
+ function derive(schema) {
8197
+ const def = defOf(schema);
8198
+ switch (def.type) {
8199
+ case "object": {
8200
+ const shape = def.shape;
8201
+ const next = {};
8202
+ for (const key of Object.keys(shape)) next[key] = readTolerant(shape[key]);
8203
+ return cloneWith(schema, {
8204
+ ...def,
8205
+ shape: next,
8206
+ catchall: void 0
8207
+ });
8208
+ }
8209
+ case "array": return cloneWith(schema, {
8210
+ ...def,
8211
+ element: readTolerant(def.element)
8212
+ });
8213
+ case "optional":
8214
+ case "nullable":
8215
+ case "readonly":
8216
+ case "default":
8217
+ case "prefault":
8218
+ case "nonoptional":
8219
+ case "catch": return cloneWith(schema, {
8220
+ ...def,
8221
+ innerType: readTolerant(def.innerType)
8222
+ });
8223
+ case "union": return cloneWith(schema, {
8224
+ ...def,
8225
+ options: def.options.map((option) => readTolerant(option))
8226
+ });
8227
+ case "record": return cloneWith(schema, {
8228
+ ...def,
8229
+ valueType: readTolerant(def.valueType)
8230
+ });
8231
+ case "tuple": return cloneWith(schema, {
8232
+ ...def,
8233
+ items: def.items.map((item) => readTolerant(item))
8234
+ });
8235
+ case "pipe": return cloneWith(schema, {
8236
+ ...def,
8237
+ in: readTolerant(def.in),
8238
+ out: readTolerant(def.out)
8239
+ });
8240
+ default: return schema;
8241
+ }
8242
+ }
8243
+ /** The same schema, reading what a device receives: unknown keys dropped, known ones as strict as before. */
8244
+ function readTolerant(schema) {
8245
+ const known = cache.get(schema);
8246
+ if (known !== void 0) return known;
8247
+ const derived = derive(schema);
8248
+ cache.set(schema, derived);
8249
+ return derived;
8250
+ }
8251
+ //#endregion
8252
+ //#region ../../packages/protocol/dist/machines.js
8253
+ /**
8254
+ * Several machines for one account: what a machine says about itself (end-to-end encrypted, the
8255
+ * relay never reads it), the choice of a product's main bridge, and the rules that route an order.
8256
+ *
8257
+ * Generic on purpose: schemas and small pure rules only, no transport and no account.
8258
+ *
8259
+ * Reading rule: a device ignores the optional fields of a report it does not know and validates
8260
+ * those it knows (`MachineReportReader`); an order a machine receives stays strict.
8261
+ */
8262
+ /** Event types of the machines, each at version 1. */
8263
+ const MACHINE_EVENT_TYPES = {
8264
+ /** The main bridge of the product changed: written by the machine that becomes main. */
8265
+ primaryChanged: "bridge.primary_changed" };
8266
+ /**
8267
+ * `payload` of `bridge.primary_changed`: the bridge that is now the product's main one, and the
8268
+ * one that was (null for the first choice). Only the last one in projection order counts.
8269
+ */
8270
+ const PrimaryChangedPayloadSchema = z.strictObject({
8271
+ bridge: z.string().regex(BRIDGE_ID_PATTERN),
8272
+ previous: z.string().regex(BRIDGE_ID_PATTERN).nullable()
8273
+ });
8274
+ /** One AI tool the bridge detected on its machine, and how it is integrated. */
8275
+ const DetectedToolSchema = z.strictObject({
8276
+ tool: z.string().min(1).max(40),
8277
+ /** The integration mode, in plain words (never a path). */
8278
+ mode: z.string().min(1).max(60),
8279
+ active: z.boolean()
8280
+ });
8281
+ /**
8282
+ * What a bridge says of its machine, inside its end-to-end encrypted beat: its version, the
8283
+ * protocol version it speaks, the products it serves (names the creator knows), how many agent
8284
+ * instances run (an instance is an agent on a story), the tools it detected and the role the
8285
+ * creator gave it. The relay sees none of it.
8286
+ */
8287
+ const MachineReportSchema = z.strictObject({
8288
+ /** The name the machine goes by (its host name unless the creator changed it). */
8289
+ name: z.string().min(1).max(60),
8290
+ bridge_version: SemverSchema,
8291
+ protocol_version: z.number().int().positive(),
8292
+ products: z.array(z.strictObject({
8293
+ id: z.string().min(1).max(80),
8294
+ name: z.string().min(1).max(80),
8295
+ /** The creator's priority for this product (1 first); absent while they gave none. */
8296
+ rank: z.int().min(1).max(9999).optional(),
8297
+ /** The marks the creator gave it (stage, pause, archive), its repository and its profile. */
8298
+ ...ProductLifeSchema.shape
8299
+ })).max(64),
8300
+ agents: z.number().int().nonnegative(),
8301
+ tools: z.array(DetectedToolSchema).max(64),
8302
+ role: z.string().max(120).optional(),
8303
+ /**
8304
+ * The names of the tokens and logins the creator gave this machine (`forge.github`,
8305
+ * `data.crm`), never their values: what to renew at the platforms if the machine is lost. Only
8306
+ * present when there is at least one.
8307
+ */
8308
+ secrets: z.array(z.string().regex(/^(?:forge|data)\.[a-z0-9][a-z0-9._-]{0,63}$/)).max(64).optional(),
8309
+ /**
8310
+ * The code platforms this machine is linked to (or being linked to): who, how far, until when.
8311
+ * Never a token. Only present when there is at least one.
8312
+ */
8313
+ forges: z.array(ForgeLinkStatusSchema).max(16).optional(),
8314
+ /**
8315
+ * Where this machine keeps its keys: the system keychain, or the fallback store of a machine
8316
+ * that has none (a server). Absent from a bridge that does not say.
8317
+ */
8318
+ key_store: z.enum(["system", "fallback"]).optional(),
8319
+ /**
8320
+ * The places this machine allows new products in, with the repositories already there that a
8321
+ * product can start from. Only present on a machine that can create products.
8322
+ */
8323
+ locations: z.array(ProductLocationSchema).max(8).optional(),
8324
+ /** The products this machine is creating or failed to create. Only present when there is one. */
8325
+ creations: z.array(ProductCreationSchema).max(8).optional()
8326
+ });
8327
+ readTolerant(MachineReportSchema);
8328
+ z.strictObject({
8329
+ bridge: z.string().regex(BRIDGE_ID_PATTERN),
8330
+ role: z.string().max(120)
8331
+ });
6769
8332
  //#endregion
6770
8333
  //#region ../../packages/protocol/dist/merge-order.js
6771
8334
  /**
@@ -6927,6 +8490,196 @@ const MethodMigratedPayloadSchema = z.strictObject({
6927
8490
  steps: z.array(MigrationStepIdSchema).max(1e3)
6928
8491
  }).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"] });
6929
8492
  //#endregion
8493
+ //#region ../../packages/protocol/dist/orders.js
8494
+ /**
8495
+ * Signed orders: every command sent to a machine is signed by the device of the creator that gives
8496
+ * it, and the bridge that receives it checks it with the key its journal registers for that
8497
+ * device, never with a key the relay supplied. An order is one JSON object; the signature covers
8498
+ * its canonical form (RFC 8785) without the signature itself, `t` included. `t` names the order
8499
+ * type and its version (`order.pair.offer/v1`), so that a signature made for an order is never
8500
+ * taken for an authorization, a message or an event, and conversely.
8501
+ *
8502
+ * Adding an order type means declaring its parameters, its target and its risk level in an
8503
+ * `OrderTypeRegistry`; no check below changes.
8504
+ */
8505
+ /** Event type the bridge writes once an order is applied: it keeps the signed order. */
8506
+ const ORDER_EVENT_TYPES = {
8507
+ applied: "order.applied",
8508
+ /** A signed order the bridge refused: the journal keeps who gave it and why it was refused. */
8509
+ rejected: "order.rejected"
8510
+ };
8511
+ /** Orders are signed with Ed25519 only. */
8512
+ const ORDER_ALG = DEVICE_SIG_ALG;
8513
+ /** The label of the receipt a bridge signs for an order. */
8514
+ const ORDER_RECEIPT_TAG = "order.receipt/v1";
8515
+ const ORDER_TAG = /^order\.([a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*)\/v([1-9][0-9]{0,2})$/;
8516
+ /** The codes a bridge answers an order with, in the order the bridge applies its checks. */
8517
+ const ORDER_ERROR_CODES = {
8518
+ INVALID_ORDER: "INVALID_ORDER",
8519
+ UPGRADE_REQUIRED: ErrorCode.UPGRADE_REQUIRED,
8520
+ UNKNOWN_DEVICE: "UNKNOWN_DEVICE",
8521
+ SIGNATURE_INVALID: ErrorCode.SIGNATURE_INVALID,
8522
+ SECURITY_ROOT_CHANGED: ErrorCode.SECURITY_ROOT_CHANGED,
8523
+ DEVICE_REVOKED: "DEVICE_REVOKED",
8524
+ WRONG_BRIDGE: "WRONG_BRIDGE",
8525
+ ORDER_EXPIRED: "ORDER_EXPIRED",
8526
+ ORDER_REPLAYED: "ORDER_REPLAYED",
8527
+ NOT_OWNER: "NOT_OWNER",
8528
+ STALE_BASIS: "STALE_BASIS",
8529
+ /** Two orders on the same target: the first one applied wins, the second gets this. */
8530
+ ALREADY_RESOLVED: "ALREADY_RESOLVED"
8531
+ };
8532
+ 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;
8533
+ /** `sha256:` then the digest in base64url: the fingerprint of what the creator saw. */
8534
+ const ORDER_BASIS_PATTERN = TRUST_EVENT_HASH_PATTERN;
8535
+ z.strictObject({
8536
+ path: z.string().min(1).max(1024),
8537
+ branch: z.string().min(1).max(255),
8538
+ blob_sha: z.string().regex(/^[0-9a-f]{40}(?:[0-9a-f]{24})?$/)
8539
+ });
8540
+ const utcDatetime = z.iso.datetime();
8541
+ /**
8542
+ * An order, as it travels. `params` is checked again by the schema of its type (a registry
8543
+ * declares it); here it is only a JSON object.
8544
+ */
8545
+ const OrderSchema = z.strictObject({
8546
+ /** `order.<type>/v<version>`. */
8547
+ t: z.string().regex(ORDER_TAG),
8548
+ id: UlidSchema,
8549
+ /** The device of the creator that signs the order. */
8550
+ device: z.string().regex(DEVICE_ID_PATTERN),
8551
+ /** The bridge the order is for. */
8552
+ bridge: z.string().regex(BRIDGE_ID_PATTERN),
8553
+ product: UlidSchema,
8554
+ /** The story the order is about, or null. */
8555
+ target: z.string().min(1).max(120).nullable(),
8556
+ /** The fingerprint of the state and documents the creator saw, or null. */
8557
+ basis: z.string().regex(ORDER_BASIS_PATTERN).nullable(),
8558
+ /** The device's own counter: strictly increasing, one per order the device signs. */
8559
+ seq: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER),
8560
+ issued_at: utcDatetime,
8561
+ expires_at: utcDatetime,
8562
+ params: z.record(z.string(), z.unknown()),
8563
+ alg: z.literal(ORDER_ALG),
8564
+ sig: Ed25519SignatureSchema
8565
+ }).refine((order) => Date.parse(order.expires_at) > Date.parse(order.issued_at), {
8566
+ path: ["expires_at"],
8567
+ message: "an order expires after it is issued"
8568
+ }).refine((order) => jsonValueIssue(order.params) === void 0, {
8569
+ path: ["params"],
8570
+ message: "params are JSON"
8571
+ });
8572
+ z.strictObject({
8573
+ t: z.literal(ORDER_RECEIPT_TAG),
8574
+ order: UlidSchema,
8575
+ /** The device that gave the order: the receipt is for it only. */
8576
+ device: z.string().regex(DEVICE_ID_PATTERN),
8577
+ bridge: z.string().regex(BRIDGE_ID_PATTERN),
8578
+ product: UlidSchema,
8579
+ outcome: z.enum(["applied", "rejected"]),
8580
+ /** The code the emitting device sees (never `UNKNOWN_DEVICE`); present when rejected. */
8581
+ code: z.string().regex(/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$/).optional(),
8582
+ /**
8583
+ * A simple handle on what an applied order made (never its content): for a secret, the
8584
+ * reference under which the machine keeps it.
8585
+ */
8586
+ reference: z.string().min(1).max(128).optional(),
8587
+ at: utcDatetime,
8588
+ alg: z.literal(ORDER_ALG),
8589
+ sig: Ed25519SignatureSchema
8590
+ }).refine((receipt) => receipt.outcome === "rejected" === (receipt.code !== void 0), {
8591
+ path: ["code"],
8592
+ message: "a rejected receipt names its code, an applied one has none"
8593
+ }).refine((receipt) => receipt.reference === void 0 || receipt.outcome === "applied", {
8594
+ path: ["reference"],
8595
+ message: "only an applied receipt carries a reference"
8596
+ }).refine((receipt) => receipt.code !== ORDER_ERROR_CODES.UNKNOWN_DEVICE, {
8597
+ path: ["code"],
8598
+ message: "the external code never tells that a device is unknown"
8599
+ });
8600
+ /** Where an order was given from, as the bridge knows it from the device's kind in its registry. */
8601
+ const ORDER_SOURCES = [
8602
+ "web",
8603
+ "mobile",
8604
+ "machine"
8605
+ ];
8606
+ /** `payload` of `order.applied` v1: the signed order, kept so that anyone can check it without the bridge. */
8607
+ const OrderAppliedPayloadSchema = z.strictObject({
8608
+ order: OrderSchema,
8609
+ /** The emitting device (the order's own `device`). */
8610
+ device: z.string().regex(DEVICE_ID_PATTERN),
8611
+ /** Fixed by the way in (the kind of device in the registry), never read from the order. */
8612
+ source: z.enum(ORDER_SOURCES),
8613
+ applied_at: utcDatetime
8614
+ });
8615
+ /**
8616
+ * `payload` of `order.rejected` v1: an order whose signature checked out and that the bridge did
8617
+ * not apply, with the code the creator sees (never `UNKNOWN_DEVICE`). It carries no `params`: a
8618
+ * refusal is a fact, not a second copy of the order.
8619
+ */
8620
+ const OrderRejectedPayloadSchema = z.strictObject({
8621
+ order: UlidSchema,
8622
+ /** `order.<type>/v<version>`. */
8623
+ t: z.string().regex(ORDER_TAG),
8624
+ device: z.string().regex(DEVICE_ID_PATTERN),
8625
+ code: z.string().regex(/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$/),
8626
+ source: z.enum(ORDER_SOURCES),
8627
+ rejected_at: utcDatetime
8628
+ });
8629
+ /**
8630
+ * A link or a QR code for one more browser or phone: the rendezvous and the secret the device drew,
8631
+ * and the device's own signature of the authorization the bridge will write `device.paired` on
8632
+ * (`offer_sig`, over the `pair.offer` record of the kind of device, this bridge, `x` and `expires_at`).
8633
+ */
8634
+ const PairOfferLinkParamsSchema = z.strictObject({
8635
+ kind: z.enum(["link", "phone"]),
8636
+ x: z.string().length(22).refine(isCanonicalBase64url),
8637
+ s: z.string().length(22).refine(isCanonicalBase64url),
8638
+ expires_at: utcDatetime,
8639
+ offer_sig: Ed25519SignatureSchema
8640
+ });
8641
+ /** The short code of another machine: an address of 8 symbols and a password of 6. */
8642
+ const PairOfferCodeParamsSchema = z.strictObject({
8643
+ kind: z.literal("machine"),
8644
+ x: z.string().regex(/^[0-9A-HJKMNP-TV-Z]{8}$/),
8645
+ w: z.string().regex(/^[0-9A-HJKMNP-TV-Z]{6}$/),
8646
+ expires_at: utcDatetime,
8647
+ offer_sig: Ed25519SignatureSchema
8648
+ });
8649
+ z.union([PairOfferLinkParamsSchema, PairOfferCodeParamsSchema]);
8650
+ 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" }) });
8651
+ z.strictObject({ role: z.string().max(120) });
8652
+ z.strictObject({});
8653
+ /** The kinds of connection a secret belongs to, and the shape of its name (`forge.github`, `data.crm`). */
8654
+ const SECRET_NAME_PATTERN = /^(?:forge|data)\.[a-z0-9][a-z0-9._-]{0,63}$/;
8655
+ /** 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. */
8656
+ const SEALED_SECRET_MAX_CHARS = Math.ceil(262784 / 3) + 80;
8657
+ z.strictObject({
8658
+ name: z.string().regex(SECRET_NAME_PATTERN),
8659
+ sealed: z.string().min(1).max(SEALED_SECRET_MAX_CHARS).regex(/^[A-Za-z0-9_-]+$/)
8660
+ });
8661
+ z.strictObject({ ranks: z.record(UlidSchema, z.int().min(1).max(9999)).refine((all) => Object.keys(all).length <= 64, { message: "at most 64 products" }) });
8662
+ /**
8663
+ * The creator removes a device (a phone, a browser or a machine, possibly the one that gives the
8664
+ * order). The device signs the `device.revoked` authorization the bridge will write on its
8665
+ * authority (over the record of the removed device, the head of its chain, the date and the
8666
+ * author, which is the device that gives the order), so that the journal proves who removed what
8667
+ * without the bridge's word. `chain_head` is the last event of a removed machine's chain the author
8668
+ * saw in a product's journal (each journal has its own chain), null for a phone or a browser.
8669
+ * `authorizations` holds one signature per product the author saw the machine in, and `*` for any
8670
+ * other product (always with a null head). The order is for the bridge that applies it, which
8671
+ * writes the removal in every product it serves.
8672
+ */
8673
+ const RevocationAuthorizationSchema = z.strictObject({
8674
+ chain_head: z.string().regex(TRUST_EVENT_HASH_PATTERN).nullable(),
8675
+ sig: Ed25519SignatureSchema
8676
+ });
8677
+ z.strictObject({
8678
+ device: z.string().regex(PAIRED_DEVICE_ID_PATTERN),
8679
+ issued_at: utcDatetime,
8680
+ 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" })
8681
+ });
8682
+ //#endregion
6930
8683
  //#region ../../packages/protocol/dist/product-key.js
6931
8684
  /**
6932
8685
  * The product key: a symmetric key of 32 random bytes, kept in the system keychain of
@@ -6936,12 +8689,21 @@ const MethodMigratedPayloadSchema = z.strictObject({
6936
8689
  * written: by `product.key_created` in the journal, and in each encrypted value.
6937
8690
  */
6938
8691
  /** 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" };
8692
+ const PRODUCT_KEY_EVENT_TYPES = {
8693
+ /** The product key was created: its `key_id`, signed by the machine that created it. */
8694
+ keyCreated: "product.key_created",
8695
+ /**
8696
+ * The product key was renewed: a new key follows the one named by `previous_key_id`. The old key
8697
+ * stays valid for reading everything written before; only new content is written under the new
8698
+ * one. Each trusted machine gets the new key in a box sealed for its own encryption key.
8699
+ */
8700
+ keyRotated: "product.key_rotated"
8701
+ };
6942
8702
  /** `pk:` then the first 16 base64url characters of the key's `apkeyid1` subkey (`crypto_kdf`). */
6943
8703
  const KEY_ID_PATTERN = /^pk:[A-Za-z0-9_-]{16}$/;
6944
8704
  const KeyIdSchema = z.string().regex(KEY_ID_PATTERN);
8705
+ /** 32 bytes in base64url: 43 characters, the last one carrying 4 bits only. */
8706
+ const PROOF_PATTERN = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
6945
8707
  /**
6946
8708
  * `payload` of `product.key_created`: the `key_id`, and the `proof` that its writer held the key
6947
8709
  * (keyed BLAKE2b of `agentplugged/key-proof/v1\0` + the product id, by the key's `approof1`
@@ -6949,7 +8711,33 @@ const KeyIdSchema = z.string().regex(KEY_ID_PATTERN);
6949
8711
  */
6950
8712
  const ProductKeyCreatedPayloadSchema = z.strictObject({
6951
8713
  key_id: KeyIdSchema,
6952
- proof: z.string().regex(/^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/)
8714
+ proof: z.string().regex(PROOF_PATTERN)
8715
+ });
8716
+ /** A trusted machine's identifier, the name of its box in a renewal. */
8717
+ const MACHINE_BOX_OWNER = /^machine:[A-Za-z0-9_-]{16}$/;
8718
+ /** A sealed box (libsodium `crypto_box_seal`) in base64url: at most 256 characters. */
8719
+ const KEY_BOX = z.string().regex(/^[A-Za-z0-9_-]{60,256}$/);
8720
+ /**
8721
+ * `payload` of `product.key_rotated`: the new `key_id`, the key it follows, the `proof` that the
8722
+ * writer held the new key (the same proof as the first announcement), and `boxes`: the new key
8723
+ * sealed, one box per trusted machine, for that machine's encryption key. No key is ever in clear.
8724
+ */
8725
+ const ProductKeyRotatedPayloadSchema = z.strictObject({
8726
+ key_id: KeyIdSchema,
8727
+ previous_key_id: KeyIdSchema,
8728
+ proof: z.string().regex(PROOF_PATTERN),
8729
+ /**
8730
+ * The proof that the writer held the key this one follows (made with that key, for this renewal
8731
+ * only): a machine that holds the previous key ignores a renewal that does not carry it.
8732
+ */
8733
+ chain_proof: z.string().regex(PROOF_PATTERN),
8734
+ /**
8735
+ * The signed order that asked for the renewal, when it came from a creator's device: the
8736
+ * `order.applied` of the same journal keeps that order. A renewal run again for the same order
8737
+ * is recognised by it and not made twice.
8738
+ */
8739
+ order: z.string().regex(/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/).optional(),
8740
+ boxes: z.record(z.string().regex(MACHINE_BOX_OWNER), KEY_BOX).refine((boxes) => Object.keys(boxes).length <= 64, { message: `at most 64 boxes` })
6953
8741
  });
6954
8742
  /** The algorithm of `payload_enc` and of the sensitive files: XChaCha20-Poly1305 (IETF). */
6955
8743
  const PAYLOAD_ENCRYPTION_ALG = "xchacha20poly1305";
@@ -6970,6 +8758,8 @@ const MAX_SENSITIVE_CONTENT_BYTES = 262144;
6970
8758
  const ENCRYPTED_FILE_PREFIX = "agentplugged-enc:1:";
6971
8759
  /** Prefix of an exported product key (`key export`). */
6972
8760
  const PRODUCT_KEY_EXPORT_PREFIX = "apk1:";
8761
+ /** Prefix of an export that holds several keys: the current one first, then the older ones. */
8762
+ const PRODUCT_KEYS_EXPORT_PREFIX = "apk2:";
6973
8763
  /** Why a path given to a sensitive file tool is refused. */
6974
8764
  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
8765
  /** A file name part: no control character, no character Windows refuses, not `.` or `..`. */
@@ -7043,8 +8833,8 @@ const READINESS_GESTURES = ["fix", "accept"];
7043
8833
  /** The option of the decision a blocking verdict opens by which the creator goes on all the same. */
7044
8834
  const READINESS_GO_ON = "go_on";
7045
8835
  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);
8836
+ const NO_CONTROL$1 = { message: "expected no control character" };
8837
+ 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
8838
  /**
7049
8839
  * One judged finding as the report gives it. `passages` are `<document>: <passage>`: the document
7050
8840
  * by its id or its file name, then a heading or an identifier (`EF-n`, `ENF-n`, `PU-n`, `AD-n`) it
@@ -7395,8 +9185,8 @@ const REGULATED_FIELDS = [
7395
9185
  "health",
7396
9186
  "finance"
7397
9187
  ];
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);
9188
+ const NO_CONTROL = { message: "expected no control character" };
9189
+ 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
9190
  /** A guest's identifier: `guest-` and a slug, so it is never mistaken for an agent of the team. */
7401
9191
  const GuestIdSchema = z.string().max(48).regex(/^guest-[a-z0-9]+(?:-[a-z0-9]+)*$/);
7402
9192
  /** A saved room's identifier: the name of its files. */
@@ -7769,117 +9559,38 @@ const ReviewTestsCheckSchema = z.strictObject({
7769
9559
  const ReviewTestsInputSchema = z.strictObject({
7770
9560
  agent: AgentIdSchema.max(64),
7771
9561
  story: StoryIdSchema.optional(),
7772
- check: ReviewTestsCheckSchema.optional()
7773
- });
7774
- /** `payload` of `tests.reviewed`: the marks, and what the mutation check found. */
7775
- const TestsReviewedPayloadSchema = z.strictObject({
7776
- story: StoryIdSchema.optional(),
7777
- commit: CommitIdSchema.optional(),
7778
- score: z.number().int().min(0).max(100).optional(),
7779
- 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
9562
+ check: ReviewTestsCheckSchema.optional()
9563
+ });
9564
+ /** `payload` of `tests.reviewed`: the marks, and what the mutation check found. */
9565
+ const TestsReviewedPayloadSchema = z.strictObject({
9566
+ story: StoryIdSchema.optional(),
9567
+ commit: CommitIdSchema.optional(),
9568
+ score: z.number().int().min(0).max(100).optional(),
9569
+ grade: z.enum([
9570
+ "A",
9571
+ "B",
9572
+ "C",
9573
+ "D",
9574
+ "F"
9575
+ ]).optional(),
9576
+ verdict: z.enum([
9577
+ "sound",
9578
+ "to_improve",
9579
+ "to_fix",
9580
+ "blocked"
9581
+ ]).optional(),
9582
+ criteria: z.array(z.strictObject({
9583
+ criterion: z.enum(TEST_CRITERIA),
9584
+ score: z.number().int().min(0).max(100)
9585
+ })).max(TEST_CRITERIA.length).optional(),
9586
+ findings: z.number().int().min(0).max(60).optional(),
9587
+ mutation: z.strictObject({
9588
+ ran: z.number().int().min(0).max(30),
9589
+ killed: z.number().int().min(0).max(30),
9590
+ survived: z.number().int().min(0).max(30)
9591
+ }).optional(),
9592
+ gaps: z.number().int().min(0).max(1e3)
7878
9593
  });
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
9594
  //#endregion
7884
9595
  //#region ../../packages/protocol/dist/usage.js
7885
9596
  /**
@@ -8030,6 +9741,12 @@ const EVENT_TYPE_REGISTRY = {
8030
9741
  version: currentVersionOf(AGENT_EVENT_TYPES.statusReported),
8031
9742
  payload: StatusReportedPayloadSchema
8032
9743
  },
9744
+ /** An instruction of the creator to an agent: their own words, encrypted with the product key. */
9745
+ [CHAIN_EVENT_TYPES.instructionGiven]: {
9746
+ version: 1,
9747
+ payload: InstructionGivenPayloadSchema,
9748
+ sensitive: true
9749
+ },
8033
9750
  [AGENT_EVENT_TYPES.progressReported]: {
8034
9751
  version: 1,
8035
9752
  payload: ProgressReportedPayloadSchema
@@ -8075,6 +9792,10 @@ const EVENT_TYPE_REGISTRY = {
8075
9792
  version: 1,
8076
9793
  payload: ProductKeyCreatedPayloadSchema
8077
9794
  },
9795
+ [PRODUCT_KEY_EVENT_TYPES.keyRotated]: {
9796
+ version: 1,
9797
+ payload: ProductKeyRotatedPayloadSchema
9798
+ },
8078
9799
  /**
8079
9800
  * The registry of trusted devices: a device paired, revoked, confirmed, the
8080
9801
  * root certified by the first phone, and an alert dismissed by the creator. Clear: every
@@ -8100,6 +9821,21 @@ const EVENT_TYPE_REGISTRY = {
8100
9821
  version: 1,
8101
9822
  payload: TrustDismissedPayloadSchema
8102
9823
  },
9824
+ /** The main bridge of the product, chosen by the creator. */
9825
+ [MACHINE_EVENT_TYPES.primaryChanged]: {
9826
+ version: 1,
9827
+ payload: PrimaryChangedPayloadSchema
9828
+ },
9829
+ /** An order a bridge applied, with the creator's signed order kept as the proof. */
9830
+ [ORDER_EVENT_TYPES.applied]: {
9831
+ version: 1,
9832
+ payload: OrderAppliedPayloadSchema
9833
+ },
9834
+ /** A signed order a bridge refused, with the code the creator saw. */
9835
+ [ORDER_EVENT_TYPES.rejected]: {
9836
+ version: 1,
9837
+ payload: OrderRejectedPayloadSchema
9838
+ },
8103
9839
  /** Reserved to tests: any payload. */
8104
9840
  [TEST_EVENT_TYPE]: {
8105
9841
  version: 1,
@@ -8310,6 +10046,26 @@ const EVENT_TYPE_REGISTRY = {
8310
10046
  version: currentVersionOf(ELICITATION_EVENT_TYPES.answered),
8311
10047
  payload: CheckpointElicitedPayloadSchema
8312
10048
  },
10049
+ /** The creator's answer carried by the signed order of a browser (the web view). */
10050
+ [WEB_ANSWER_EVENT_TYPES.answered]: {
10051
+ version: currentVersionOf(WEB_ANSWER_EVENT_TYPES.answered),
10052
+ payload: CheckpointWebAnsweredPayloadSchema
10053
+ },
10054
+ /** An answer from the web held for the undo window: the point stays open meanwhile. */
10055
+ [WEB_ANSWER_EVENT_TYPES.held]: {
10056
+ version: currentVersionOf(WEB_ANSWER_EVENT_TYPES.held),
10057
+ payload: CheckpointWebHeldPayloadSchema
10058
+ },
10059
+ /** A held answer taken back by the creator: the point was never answered. */
10060
+ [WEB_ANSWER_EVENT_TYPES.holdCancelled]: {
10061
+ version: 1,
10062
+ payload: CheckpointWebHoldCancelledPayloadSchema
10063
+ },
10064
+ /** A held answer the machine could not carry out: released, the point is open again and says why. */
10065
+ [WEB_ANSWER_EVENT_TYPES.holdFailed]: {
10066
+ version: 1,
10067
+ payload: CheckpointWebHoldFailedPayloadSchema
10068
+ },
8313
10069
  /** A story merged by the engine once the creator approved its merge checkpoint. */
8314
10070
  [MERGE_EVENT_TYPES.merged]: {
8315
10071
  version: currentVersionOf(MERGE_EVENT_TYPES.merged),
@@ -8481,6 +10237,437 @@ function currentEvent(event) {
8481
10237
  event: current
8482
10238
  };
8483
10239
  }
10240
+ /** The request shape (without the identifier, the version and the product, which the file reading adds). */
10241
+ const FindRequestFields = {
10242
+ op: z.literal("find"),
10243
+ query: z.string().min(1).max(120)
10244
+ };
10245
+ /** A place in a document where the words are: the document, its section, the line and the words around. */
10246
+ const FindHitSchema = z.strictObject({
10247
+ /** The path from the product's folder, with `/`. */
10248
+ path: z.string().min(1).max(300),
10249
+ /** True for a page of the knowledge base (it opens in the knowledge base, not in the documents). */
10250
+ kb: z.literal(true).optional(),
10251
+ title: z.string().min(1).max(200),
10252
+ /** The heading of the section, when it has one. */
10253
+ heading: z.string().max(200).optional(),
10254
+ /** The line of the document where the words are, from 1. */
10255
+ line: z.int().min(1).max(1e7),
10256
+ /** The line itself, cut around the words. */
10257
+ snippet: z.string().min(1).max(240)
10258
+ });
10259
+ const FindDataSchema = z.strictObject({
10260
+ op: z.literal("find"),
10261
+ hits: z.array(FindHitSchema).max(8),
10262
+ /** True when more documents hold the words than the answer lists. */
10263
+ truncated: z.boolean(),
10264
+ /** True when some document could not be read in full: the answer may miss some. */
10265
+ incomplete: z.boolean().optional()
10266
+ });
10267
+ const KB_CLAIM_MAX_CHARS = 1200;
10268
+ /** The request shapes (without the identifier, the version and the product, which the file reading adds). */
10269
+ const KbListRequestFields = { op: z.literal("kb") };
10270
+ const KbAskRequestFields = {
10271
+ op: z.literal("ask"),
10272
+ /** Chosen by the device: asking again with it reads the same question's state, it never starts a second one. */
10273
+ ask: z.string().regex(ULID_PATTERN),
10274
+ question: z.string().min(1).max(800)
10275
+ };
10276
+ /** One page of the knowledge base. */
10277
+ const KbPageSchema = z.strictObject({
10278
+ /** The path from the product's folder, with `/`. */
10279
+ path: z.string().min(1).max(300),
10280
+ title: z.string().min(1).max(200),
10281
+ /** The first lines of the page, as written. */
10282
+ summary: z.string().max(300).optional(),
10283
+ /** The day the page says it was last updated (`2026-10-01`), when it says one. */
10284
+ updated: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
10285
+ /** The last story the page says it took in (`2-3`), when it says one. */
10286
+ story: z.string().regex(/^[1-9]\d{0,5}-[1-9]\d{0,5}$/).optional()
10287
+ });
10288
+ const KbListDataSchema = z.strictObject({
10289
+ op: z.literal("kb"),
10290
+ pages: z.array(KbPageSchema).max(200),
10291
+ /** True when the product holds more pages than the bridge lists. */
10292
+ truncated: z.boolean(),
10293
+ /** True when some page could not be read in full: the list may miss some. */
10294
+ incomplete: z.boolean().optional(),
10295
+ /** Whether the machine can answer a question: an AI tool it knows how to run is there. */
10296
+ askable: z.boolean()
10297
+ });
10298
+ /** A place of the base a statement stands on: the page, the section, the line and the words. */
10299
+ const KbCitationSchema = z.strictObject({
10300
+ path: z.string().min(1).max(300),
10301
+ /** The heading of the section the passage is in, when it has one. */
10302
+ heading: z.string().max(200).optional(),
10303
+ /** The line of the page where the quotation starts, from 1. */
10304
+ line: z.int().min(1).max(1e7),
10305
+ /** The words of the page, as they are written there. */
10306
+ quote: z.string().min(1).max(300)
10307
+ });
10308
+ const KbClaimSchema = z.strictObject({
10309
+ text: z.string().min(1).max(KB_CLAIM_MAX_CHARS),
10310
+ /** At least one: a statement without a source that holds never leaves the bridge. */
10311
+ citations: z.array(KbCitationSchema).min(1).max(4)
10312
+ });
10313
+ const KbCostSchema = z.strictObject({
10314
+ usd: z.number().min(0).max(1e6).optional(),
10315
+ basis: z.string().min(1).max(16),
10316
+ tokens_in: z.int().min(0).max(1e9).optional(),
10317
+ tokens_out: z.int().min(0).max(1e9).optional()
10318
+ });
10319
+ const KbAskDataSchema = z.strictObject({
10320
+ op: z.literal("ask"),
10321
+ ask: z.string().regex(ULID_PATTERN),
10322
+ /** `working`: ask again; `answered`: the answer is below; `failed`: `failure` says why. */
10323
+ state: z.enum([
10324
+ "working",
10325
+ "answered",
10326
+ "failed"
10327
+ ]),
10328
+ failure: z.string().min(1).max(32).optional(),
10329
+ answer: z.strictObject({
10330
+ claims: z.array(KbClaimSchema).max(12),
10331
+ /** True when nothing in the base answers the question (or nothing that was said held). */
10332
+ no_source: z.boolean(),
10333
+ /** How many statements the bridge removed because no citation of theirs held. */
10334
+ dropped: z.int().min(0).max(1e3)
10335
+ }).optional(),
10336
+ /** How many passages of the base the AI tool was given (0: it was not asked). */
10337
+ passages: z.int().min(0).max(100),
10338
+ cost: KbCostSchema.optional()
10339
+ });
10340
+ /** The local reference holding the last journal commit projected: never pushed. */
10341
+ const JOURNAL_PROJECTED_REF = "refs/agentplugged/projected";
10342
+ const IgnoredEventSchema = z.strictObject({
10343
+ id: z.string(),
10344
+ reason: z.enum([
10345
+ "unknown_signer",
10346
+ "bad_signature",
10347
+ "broken_chain",
10348
+ "wrong_product",
10349
+ "invalid",
10350
+ "key_conflict",
10351
+ "product_conflict",
10352
+ "test_type",
10353
+ "checkpoint_id_collision",
10354
+ "not_human",
10355
+ "already_resolved",
10356
+ "unendorsed_human",
10357
+ "checkpoint_unknown",
10358
+ "decrypt_failed",
10359
+ "key_proof_invalid",
10360
+ "sensitive_in_clear",
10361
+ "unexpected_encryption",
10362
+ "unsupported_alg",
10363
+ "untrusted_authorization",
10364
+ "device_revoked",
10365
+ "dismissed",
10366
+ "not_reopenable"
10367
+ ])
10368
+ });
10369
+ /** Two valid events of one device name the same `prev` (two clones on one machine): both kept. */
10370
+ const ChainForkWarningSchema = z.strictObject({
10371
+ code: z.literal("chain_fork"),
10372
+ device: z.string(),
10373
+ prev: z.string().nullable(),
10374
+ ids: z.array(z.string())
10375
+ });
10376
+ /**
10377
+ * Two `checkpoint.opened` events give the same checkpoint id: `ids[0]` opened it, the others are
10378
+ * ignored (`checkpoint_id_collision`).
10379
+ */
10380
+ const CheckpointIdCollisionWarningSchema = z.strictObject({
10381
+ code: z.literal("checkpoint_id_collision"),
10382
+ checkpoint: z.string(),
10383
+ ids: z.array(z.string())
10384
+ });
10385
+ /**
10386
+ * A signed `checkpoint.opened` asks a weaker recipient or confirmation than the rules give: the
10387
+ * projection applies the rules' values, and says so. `fields` names what was raised.
10388
+ */
10389
+ const CheckpointWeakPayloadWarningSchema = z.strictObject({
10390
+ code: z.literal("checkpoint_weak_payload"),
10391
+ checkpoint: z.string(),
10392
+ id: z.string(),
10393
+ fields: z.array(z.enum(["recipient_role", "confirmation"]))
10394
+ });
10395
+ /**
10396
+ * A `high` risk checkpoint answered with the `locale` assurance only (no `presence` credential):
10397
+ * accepted, and shown as such.
10398
+ */
10399
+ const LocaleOnHighRiskWarningSchema = z.strictObject({
10400
+ code: z.literal("locale_on_high_risk"),
10401
+ checkpoint: z.string(),
10402
+ id: z.string()
10403
+ });
10404
+ z.strictObject({
10405
+ code: z.literal("sensitive_unreadable"),
10406
+ count: z.number().int().min(1)
10407
+ });
10408
+ /**
10409
+ * Warnings drawn from the journal alone, the same on every machine. Machine-local ones (a history
10410
+ * rewritten since this machine's last projection, sensitive content this machine cannot read)
10411
+ * are never written to `state.json`.
10412
+ */
10413
+ const ProjectionWarningSchema = z.discriminatedUnion("code", [
10414
+ ChainForkWarningSchema,
10415
+ CheckpointIdCollisionWarningSchema,
10416
+ CheckpointWeakPayloadWarningSchema,
10417
+ LocaleOnHighRiskWarningSchema
10418
+ ]);
10419
+ const ProjectedDeviceSchema = z.strictObject({
10420
+ device: z.string(),
10421
+ public_key: z.string()
10422
+ });
10423
+ /**
10424
+ * `state.json`: deterministic (two machines write the same bytes from the same journal, in
10425
+ * canonical JSON), `computed_at` being the date (`at`) of the last event applied in projection
10426
+ * order, never the clock.
10427
+ */
10428
+ const ProjectionStateSchema = z.strictObject({
10429
+ format: z.literal(1),
10430
+ /** The journal position projected (a commit id in Git), or null for an empty journal. */
10431
+ journal_head: z.string().nullable(),
10432
+ /** The `at` of the last event applied, in projection order; null when none was applied. */
10433
+ computed_at: z.string().nullable(),
10434
+ /** The product id of the journal's `product.created`, or null. */
10435
+ product: z.string().nullable(),
10436
+ /** The registered machines, sorted by `device` (the creators are in `state.humans`). */
10437
+ devices: z.array(ProjectedDeviceSchema),
10438
+ /** Events applied. */
10439
+ applied: z.number().int().min(0),
10440
+ /** Events ignored, sorted by `id` then `reason`. */
10441
+ ignored: z.array(IgnoredEventSchema),
10442
+ warnings: z.array(ProjectionWarningSchema),
10443
+ /** What the reducers built. */
10444
+ state: z.record(z.string(), z.unknown())
10445
+ });
10446
+ //#endregion
10447
+ //#region ../../packages/protocol/dist/published-state.js
10448
+ /**
10449
+ * Where a view stands in a journal: `n` is the number of events the journal held (it never goes
10450
+ * down) and `heads` the head of each writer's chain.
10451
+ */
10452
+ const FeedCursorSchema = z.strictObject({
10453
+ n: z.number().int().min(0),
10454
+ heads: z.record(z.string().min(1).max(64), z.string().min(1).max(128))
10455
+ });
10456
+ /** The devices the journal trusts, as the views need them. */
10457
+ const PublishedTrustedDeviceSchema = z.strictObject({
10458
+ kind: z.enum([
10459
+ "machine",
10460
+ "human",
10461
+ "phone",
10462
+ "browser"
10463
+ ]),
10464
+ sig_key: z.string().min(1).max(128),
10465
+ enc_key: z.string().min(1).max(128).nullable(),
10466
+ provisional: z.boolean()
10467
+ });
10468
+ /**
10469
+ * Where the import of an earlier history stands: the events brought in out of those the journal
10470
+ * holds, and the totals of the product (the creator reads « 48 stories, 131 validations »).
10471
+ * `ignored` counts the events the journal holds that were refused as forged, unsigned or cut from
10472
+ * their chain: they are told, never counted among the validations.
10473
+ */
10474
+ const ImportInfoSchema = z.strictObject({
10475
+ state: z.enum([
10476
+ "none",
10477
+ "running",
10478
+ "done",
10479
+ "failed"
10480
+ ]),
10481
+ progress: z.strictObject({
10482
+ events_done: z.number().int().nonnegative(),
10483
+ events_total: z.number().int().nonnegative()
10484
+ }).optional(),
10485
+ stories: z.number().int().nonnegative().optional(),
10486
+ validations: z.number().int().nonnegative().optional(),
10487
+ ignored: z.number().int().nonnegative().optional(),
10488
+ /** Why the import stopped (a short code, never a received value); with `failed` only. */
10489
+ reason: z.string().max(100).optional()
10490
+ });
10491
+ /**
10492
+ * What the exit guard puts in place of a text it kept from leaving. A stable code, not a sentence:
10493
+ * a view recognises it and shows it in the creator's language.
10494
+ */
10495
+ const REDACTED_SECRET = "redacted:secret";
10496
+ /**
10497
+ * What the main bridge publishes of a product. The projection is the one `get_state` returns; the
10498
+ * trust registry is the journal's (never the relay's list); the machines say which bridge is
10499
+ * main; the import says where the import of an earlier history stands.
10500
+ */
10501
+ const PublishedStateSchema = z.strictObject({
10502
+ projection: ProjectionStateSchema,
10503
+ trust: z.strictObject({
10504
+ /** True while the journal's root differs from the pinned one: no order is accepted. */
10505
+ blocked: z.boolean(),
10506
+ devices: z.record(z.string().regex(DEVICE_ID_PATTERN), PublishedTrustedDeviceSchema),
10507
+ revoked: z.array(z.string().regex(DEVICE_ID_PATTERN))
10508
+ }),
10509
+ machines: z.strictObject({
10510
+ primary: z.string().regex(BRIDGE_ID_PATTERN).nullable(),
10511
+ /** True when nobody chose the main bridge: the engine elected it from the journal. */
10512
+ automatic: z.boolean()
10513
+ }),
10514
+ import: ImportInfoSchema
10515
+ });
10516
+ readTolerant(z.strictObject({
10517
+ current: z.enum(PHASE_IDS).nullable(),
10518
+ since: z.string().nullable(),
10519
+ phases: z.array(z.strictObject({
10520
+ id: z.enum(PHASE_IDS),
10521
+ status: z.enum([
10522
+ "done",
10523
+ "in_progress",
10524
+ "in_review",
10525
+ "upcoming",
10526
+ "skipped"
10527
+ ])
10528
+ })).max(PHASE_IDS.length)
10529
+ }));
10530
+ /**
10531
+ * What a story's file says, as the main bridge read it from the product's working copy (the
10532
+ * `story_docs` key of `projection.state` of the published state, set only for the files the
10533
+ * bridge could read and that follow the story contract): the title, the owner, the requirements
10534
+ * it covers, the stories it depends on, its branch and budget, its intent and its acceptance
10535
+ * criteria in order. The state's `state` record is open, so a view that does not know this key
10536
+ * still reads the rest; a view that does reads each entry with this schema and leaves out the
10537
+ * ones that do not fit.
10538
+ */
10539
+ const PublishedStoryDocSchema = z.strictObject({
10540
+ title: z.string().min(1).max(400),
10541
+ owner: z.string().min(1).max(64).optional(),
10542
+ epic: z.string().min(1).max(64).optional(),
10543
+ requirements: z.array(z.string().min(1).max(64)).max(40),
10544
+ depends_on: z.array(z.string().min(1).max(64)).max(40),
10545
+ branch: z.string().min(1).max(400).optional(),
10546
+ budget_usd: z.number().finite().nonnegative().optional(),
10547
+ intent: z.string().max(1200),
10548
+ criteria: z.array(z.string().max(400)).max(40)
10549
+ });
10550
+ readTolerant(PublishedStoryDocSchema);
10551
+ z.record(z.string().regex(/^[1-9]\d{0,5}-[1-9]\d{0,5}$/), PublishedStoryDocSchema);
10552
+ const PATH_SEGMENT_MAX = 200;
10553
+ const PATH_DEPTH_MAX = 16;
10554
+ const FEED_OPS_MAX = 2e4;
10555
+ /** One step of a change: set the value at a path, or remove it. */
10556
+ const FeedOpSchema = z.discriminatedUnion("op", [z.strictObject({
10557
+ op: z.literal("set"),
10558
+ path: z.array(z.string().max(PATH_SEGMENT_MAX)).min(1).max(PATH_DEPTH_MAX),
10559
+ value: z.unknown()
10560
+ }), z.strictObject({
10561
+ op: z.literal("remove"),
10562
+ path: z.array(z.string().max(PATH_SEGMENT_MAX)).min(1).max(PATH_DEPTH_MAX)
10563
+ })]);
10564
+ const FeedSnapshotSchema = z.strictObject({
10565
+ kind: z.literal("snapshot"),
10566
+ schema: z.number().int().positive(),
10567
+ product: z.string().regex(ULID_PATTERN),
10568
+ cursor: FeedCursorSchema,
10569
+ state: PublishedStateSchema
10570
+ });
10571
+ const FeedChangeSchema = z.strictObject({
10572
+ kind: z.literal("change"),
10573
+ schema: z.number().int().positive(),
10574
+ product: z.string().regex(ULID_PATTERN),
10575
+ from_cursor: FeedCursorSchema,
10576
+ to_cursor: FeedCursorSchema,
10577
+ ops: z.array(FeedOpSchema).max(FEED_OPS_MAX)
10578
+ });
10579
+ z.strictObject({
10580
+ /** The same for all the parts of one message. */
10581
+ id: z.string().regex(ULID_PATTERN),
10582
+ index: z.number().int().min(0).max(4095),
10583
+ count: z.number().int().min(1).max(4096),
10584
+ /** SHA-256 of the whole content, base64url (43 characters). */
10585
+ digest: z.string().length(43).refine(isCanonicalBase64url),
10586
+ data: z.string().min(1).max(262144).refine(isCanonicalBase64url)
10587
+ });
10588
+ /** The bridge's presence: signed (as the message that carries it) and dated. */
10589
+ const FeedBeatSchema = z.strictObject({
10590
+ bridge: z.string().regex(BRIDGE_ID_PATTERN),
10591
+ /** Seconds since the epoch, on the bridge's clock. */
10592
+ at: z.number().int().positive(),
10593
+ bridge_version: SemverSchema,
10594
+ protocol_version: z.number().int().positive(),
10595
+ schema: z.number().int().positive(),
10596
+ /** The last sequence number this bridge gave to a message for this view. */
10597
+ last_seq: z.number().int().min(0),
10598
+ /** The products this bridge publishes, and the cursor of each. */
10599
+ publishes: z.array(z.strictObject({
10600
+ product: z.string().regex(ULID_PATTERN),
10601
+ cursor: FeedCursorSchema
10602
+ })).max(64),
10603
+ report: MachineReportSchema.optional()
10604
+ });
10605
+ z.strictObject({
10606
+ schema: z.number().int().positive(),
10607
+ product: z.string().regex(ULID_PATTERN),
10608
+ cursor: FeedCursorSchema.nullable()
10609
+ });
10610
+ z.strictObject({
10611
+ product: z.string().regex(ULID_PATTERN),
10612
+ cursor: FeedCursorSchema
10613
+ });
10614
+ const FeedUpgradeRequiredSchema = z.strictObject({
10615
+ current: z.number().int().positive(),
10616
+ minimum: z.number().int().positive()
10617
+ });
10618
+ /**
10619
+ * What a bridge tells a device it just removed: the device it means, the product whose journal
10620
+ * holds the removal and the date. The device believes it only from the bridge it pinned when it was
10621
+ * linked (the message is signed by that bridge's key), never from the relay closing a connection.
10622
+ */
10623
+ const FeedDeviceRemovedSchema = z.strictObject({
10624
+ device: z.string().regex(DEVICE_ID_PATTERN),
10625
+ product: z.string().regex(ULID_PATTERN),
10626
+ removed_at: z.iso.datetime()
10627
+ });
10628
+ readTolerant(PublishedStateSchema);
10629
+ readTolerant(FeedSnapshotSchema);
10630
+ readTolerant(FeedChangeSchema);
10631
+ readTolerant(FeedBeatSchema);
10632
+ readTolerant(FeedUpgradeRequiredSchema);
10633
+ readTolerant(FeedDeviceRemovedSchema);
10634
+ /** The most bytes one `vault_put` or `vault_blob` frame carries (decoded). */
10635
+ const VAULT_CHUNK_BYTES = 196608;
10636
+ /** What the relay keeps apart: a full vault, or the changes after it. */
10637
+ const VAULT_KINDS = ["full", "delta"];
10638
+ const b64 = (bytes) => z.string().length(Math.ceil(bytes * 4 / 3)).refine(isCanonicalBase64url);
10639
+ /** The content key sealed for one device: an ephemeral X25519 key, a nonce and the sealed key. */
10640
+ const VaultKeyBoxSchema = z.strictObject({
10641
+ device: z.string().regex(DEVICE_ID_PATTERN),
10642
+ /** The sender's ephemeral X25519 public key (32 bytes). */
10643
+ eph: b64(32),
10644
+ /** XChaCha20-Poly1305 nonce (24 bytes). */
10645
+ nonce: b64(24),
10646
+ /** The content key (32 bytes) and the authentication tag (16 bytes). */
10647
+ box: b64(48)
10648
+ });
10649
+ z.strictObject({
10650
+ v: z.literal(1),
10651
+ product: z.string().regex(ULID_PATTERN),
10652
+ kind: z.enum(VAULT_KINDS),
10653
+ rev: z.number().int().positive(),
10654
+ base_rev: z.number().int().positive().nullable(),
10655
+ /** When the machine sealed the blob, ISO 8601. */
10656
+ at: z.iso.datetime(),
10657
+ /** The machine that sealed it, by its journal identifier. */
10658
+ bridge: z.string().regex(BRIDGE_ID_PATTERN),
10659
+ boxes: z.array(VaultKeyBoxSchema).max(64),
10660
+ /** XChaCha20-Poly1305 nonce of the content (24 bytes). */
10661
+ nonce: b64(24),
10662
+ /** The compressed, encrypted content. */
10663
+ body: z.string().min(1).refine(isCanonicalBase64url),
10664
+ /** Signature of the header and of the digest of `body`. */
10665
+ sig: z.string().length(86).refine(isCanonicalBase64url)
10666
+ });
10667
+ readTolerant(z.strictObject({
10668
+ content: z.discriminatedUnion("kind", [FeedSnapshotSchema, FeedChangeSchema]),
10669
+ report: MachineReportSchema.optional()
10670
+ }));
8484
10671
  //#endregion
8485
10672
  //#region ../../packages/protocol/dist/relay.js
8486
10673
  /**
@@ -8520,7 +10707,8 @@ const KEY_32 = z.string().length(43).refine(isCanonicalBase64url);
8520
10707
  */
8521
10708
  const RelayAttachDeclarationSchema = z.strictObject({
8522
10709
  device_id: RelayIdSchema,
8523
- kind: z.literal("bridge"),
10710
+ /** `bridge` for a machine attached during a rendezvous, `browser` for a browser registering itself. */
10711
+ kind: z.enum(["bridge", "browser"]),
8524
10712
  sig_key: KEY_32,
8525
10713
  enc_key: KEY_32,
8526
10714
  relay: z.string().min(1).max(253),
@@ -8607,15 +10795,83 @@ z.discriminatedUnion("type", [
8607
10795
  /** Opaque bytes in base64url. */
8608
10796
  data: z.string().min(1).max(base64urlLength(RENDEZVOUS_MAX_DATA_BYTES)).refine(isCanonicalBase64url)
8609
10797
  }),
10798
+ z.strictObject({
10799
+ type: z.literal("device_register"),
10800
+ declaration: RelayAttachDeclarationSchema,
10801
+ signature: SignatureSchema,
10802
+ credential: z.string().min(1).max(4096).optional()
10803
+ }),
10804
+ z.strictObject({
10805
+ type: z.literal("device_revoke"),
10806
+ sig_key: KEY_32.optional(),
10807
+ /** Instead of the key: the relay's own identifier of the device (for a device no journal knows). */
10808
+ device_id: RelayIdSchema.optional()
10809
+ }).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" }),
10810
+ z.strictObject({
10811
+ type: z.literal("vault_put"),
10812
+ product: RelayIdSchema,
10813
+ kind: z.enum(VAULT_KINDS),
10814
+ /** Rises with every blob of the product. */
10815
+ rev: z.number().int().positive(),
10816
+ /** The revision a change follows; null for a full vault. */
10817
+ base_rev: z.number().int().positive().nullable(),
10818
+ index: z.number().int().min(0).max(255),
10819
+ count: z.number().int().min(1).max(256),
10820
+ data: z.string().min(1).max(base64urlLength(VAULT_CHUNK_BYTES)).refine(isCanonicalBase64url)
10821
+ }),
10822
+ z.strictObject({
10823
+ type: z.literal("vault_get"),
10824
+ product: RelayIdSchema
10825
+ }),
10826
+ z.strictObject({ type: z.literal("outbox_list") }),
10827
+ z.strictObject({
10828
+ type: z.literal("push_register"),
10829
+ platform: z.enum(PUSH_PLATFORMS),
10830
+ token: PushTokenSchema
10831
+ }),
10832
+ z.strictObject({ type: z.literal("push_unregister") }),
10833
+ z.strictObject({
10834
+ type: z.literal("notify"),
10835
+ ...PushNoticeSchema.shape
10836
+ }),
10837
+ z.strictObject({
10838
+ type: z.literal("notify_clear"),
10839
+ id: PushNoticeSchema.shape.id
10840
+ }),
10841
+ z.strictObject({
10842
+ type: z.literal("presence_report"),
10843
+ surface: z.enum(NOTIFY_SURFACES),
10844
+ active: z.boolean()
10845
+ }),
10846
+ z.strictObject({ type: z.literal("notify_prefs_get") }),
10847
+ z.strictObject({
10848
+ type: z.literal("notify_prefs_set"),
10849
+ prefs: NotifyPrefsSchema
10850
+ }),
10851
+ z.strictObject({
10852
+ type: z.literal("outbox_cancel"),
10853
+ id: RelayIdSchema
10854
+ }),
8610
10855
  z.strictObject({
8611
10856
  type: z.literal("rv_attach"),
8612
10857
  declaration: RelayAttachDeclarationSchema,
8613
- signature: SignatureSchema
10858
+ signature: SignatureSchema,
10859
+ /**
10860
+ * A bridge already attached to the account that vouches for ANOTHER machine's declaration
10861
+ * (the machine signed it itself): the identifier of the voucher on this relay.
10862
+ */
10863
+ voucher: RelayIdSchema.optional()
8614
10864
  })
8615
10865
  ]);
8616
10866
  const PresenceEntrySchema = z.strictObject({
8617
10867
  device_id: RelayIdSchema,
8618
10868
  kind: z.enum(RELAY_DEVICE_KINDS),
10869
+ /**
10870
+ * The public signing key of the device: a device of the account's journal is recognised by it (the
10871
+ * relay's identifiers mean nothing to a journal), and a device no journal knows by its key is shown
10872
+ * as not verified.
10873
+ */
10874
+ sig_key: KEY_32.optional(),
8619
10875
  online: z.boolean(),
8620
10876
  /** ISO 8601, `null` when the device never connected. */
8621
10877
  last_seen_at: z.string().nullable()
@@ -8667,37 +10923,104 @@ z.discriminatedUnion("type", [
8667
10923
  prekey: PrekeySchema.nullable()
8668
10924
  }),
8669
10925
  z.strictObject({
8670
- type: z.literal("rv_opened"),
8671
- x: RendezvousIdSchema
10926
+ type: z.literal("rv_opened"),
10927
+ x: RendezvousIdSchema
10928
+ }),
10929
+ z.strictObject({
10930
+ type: z.literal("rv_joined"),
10931
+ x: RendezvousIdSchema,
10932
+ peer: z.string().min(1).max(32),
10933
+ open: z.boolean()
10934
+ }),
10935
+ z.strictObject({
10936
+ type: z.literal("rv_peer"),
10937
+ peer: z.string().min(1).max(32)
10938
+ }),
10939
+ z.strictObject({
10940
+ type: z.literal("rv_message"),
10941
+ id: RelayIdSchema,
10942
+ peer: z.string().min(1).max(32).optional(),
10943
+ data: z.string().min(1).max(base64urlLength(RENDEZVOUS_MAX_DATA_BYTES)).refine(isCanonicalBase64url)
10944
+ }),
10945
+ z.strictObject({
10946
+ type: z.literal("rv_sent"),
10947
+ id: RelayIdSchema,
10948
+ delivered: z.boolean()
10949
+ }),
10950
+ z.strictObject({
10951
+ type: z.literal("rv_attached"),
10952
+ device_id: RelayIdSchema
10953
+ }),
10954
+ z.strictObject({
10955
+ type: z.literal("device_registered"),
10956
+ device_id: RelayIdSchema
10957
+ }),
10958
+ z.strictObject({
10959
+ type: z.literal("device_revoked"),
10960
+ found: z.boolean(),
10961
+ /** The relay's identifier of the device it stopped serving; null when there was none. */
10962
+ device_id: RelayIdSchema.nullable()
10963
+ }),
10964
+ z.strictObject({
10965
+ type: z.literal("device_gone"),
10966
+ device_id: RelayIdSchema
10967
+ }),
10968
+ z.strictObject({
10969
+ type: z.literal("vault_stored"),
10970
+ product: RelayIdSchema,
10971
+ rev: z.number().int().positive()
10972
+ }),
10973
+ z.strictObject({
10974
+ type: z.literal("vault_blob"),
10975
+ product: RelayIdSchema,
10976
+ rev: z.number().int().positive(),
10977
+ base_rev: z.number().int().positive().nullable(),
10978
+ kind: z.enum(VAULT_KINDS),
10979
+ /** When the relay received it, ISO 8601. */
10980
+ stored_at: z.iso.datetime(),
10981
+ index: z.number().int().min(0).max(255),
10982
+ count: z.number().int().min(1).max(256),
10983
+ data: z.string().min(1).max(base64urlLength(VAULT_CHUNK_BYTES)).refine(isCanonicalBase64url)
10984
+ }),
10985
+ z.strictObject({
10986
+ type: z.literal("vault_end"),
10987
+ product: RelayIdSchema,
10988
+ blobs: z.number().int().min(0)
8672
10989
  }),
10990
+ z.strictObject({ type: z.literal("vault_cleared") }),
10991
+ z.strictObject({ type: z.literal("push_registered") }),
10992
+ z.strictObject({ type: z.literal("push_unregistered") }),
8673
10993
  z.strictObject({
8674
- type: z.literal("rv_joined"),
8675
- x: RendezvousIdSchema,
8676
- peer: z.string().min(1).max(32),
8677
- open: z.boolean()
10994
+ type: z.literal("notified"),
10995
+ id: PushNoticeSchema.shape.id,
10996
+ sent: z.number().int().min(0).max(1e3)
8678
10997
  }),
8679
10998
  z.strictObject({
8680
- type: z.literal("rv_peer"),
8681
- peer: z.string().min(1).max(32)
10999
+ type: z.literal("notify_prefs"),
11000
+ prefs: NotifyPrefsSchema
8682
11001
  }),
8683
11002
  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)
11003
+ type: z.literal("notice"),
11004
+ ...PushNoticeSchema.shape
8688
11005
  }),
8689
11006
  z.strictObject({
8690
- type: z.literal("rv_sent"),
8691
- id: RelayIdSchema,
8692
- delivered: z.boolean()
11007
+ type: z.literal("outbox"),
11008
+ items: z.array(z.strictObject({
11009
+ id: RelayIdSchema,
11010
+ to: RelayIdSchema,
11011
+ queued_at: z.iso.datetime(),
11012
+ expires_at: z.iso.datetime()
11013
+ })).max(500)
8693
11014
  }),
8694
11015
  z.strictObject({
8695
- type: z.literal("rv_attached"),
8696
- device_id: RelayIdSchema
11016
+ type: z.literal("outbox_cancelled"),
11017
+ id: RelayIdSchema,
11018
+ cancelled: z.boolean()
8697
11019
  })
8698
11020
  ]);
8699
11021
  //#endregion
8700
11022
  //#region ../../packages/protocol/dist/pairing.js
11023
+ const PRODUCT_ID = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
8701
11024
  /**
8702
11025
  * The messages of the pairing link. Generic on purpose
8703
11026
  *: schemas and codes only; the link's bytes and the boxes are in `packages/crypto`, the
@@ -8709,6 +11032,10 @@ z.discriminatedUnion("type", [
8709
11032
  * - `envelope` (bridge to browser): a message from peer to peer encrypted for the
8710
11033
  * browser's key and signed by the bridge: `link.welcome` then one `product.keys` per product;
8711
11034
  * - `refused` (bridge to browser): a code and nothing else; informative, not authenticated.
11035
+ *
11036
+ * A machine that joins with a short code does not hold a link's secret: both sides run a password
11037
+ * exchange first (`pake_*` messages), and the box key is drawn from the session key it gives. The
11038
+ * bridge then says who it is in `hello`; the machine answers with its `request`.
8712
11039
  */
8713
11040
  /** Codes of the pairing, said by the bridge (separate from `ErrorCode`, like the relay's). */
8714
11041
  const PAIR_ERROR_CODES = {
@@ -8716,22 +11043,50 @@ const PAIR_ERROR_CODES = {
8716
11043
  PAIRING_EXPIRED: "PAIRING_EXPIRED",
8717
11044
  PAIRING_INVALID_PROOF: "PAIRING_INVALID_PROOF",
8718
11045
  PAIRING_KEY_MISMATCH: "PAIRING_KEY_MISMATCH",
11046
+ /** A machine joined from a folder whose product the bridge that gave the code does not serve. */
11047
+ PAIRING_PRODUCT_UNKNOWN: "PAIRING_PRODUCT_UNKNOWN",
8719
11048
  PAIRING_WRONG_ACCOUNT: "PAIRING_WRONG_ACCOUNT"
8720
11049
  };
8721
11050
  /** The type labels of the messages (`t` of the signed ones, `type` of the boxes and messages). */
8722
11051
  const PAIR_MESSAGE_TYPES = {
8723
11052
  request: "link.request/v1",
8724
11053
  welcome: TRUST_SIGNATURE_TAGS.linkWelcome,
8725
- productKeys: "product.keys/v1"
11054
+ productKeys: "product.keys/v1",
11055
+ hello: "pair.hello/v1"
8726
11056
  };
8727
11057
  const key32 = z.string().length(43).refine(isCanonicalBase64url);
11058
+ const key22 = z.string().length(22).refine(isCanonicalBase64url);
8728
11059
  const label = z.string().min(1).max(40);
11060
+ /** The devices that answer a pairing: a browser, a phone or another machine. */
11061
+ const REQUESTER_DEVICE_ID_PATTERN = /^(?:browser|phone|machine):[A-Za-z0-9_-]{16}$/;
11062
+ const PairCodeIdSchema = z.string().regex(/^[0-9A-HJKMNP-TV-Z]{8}$/);
11063
+ const PairCodeSecretSchema = z.string().regex(/^[0-9A-HJKMNP-TV-Z]{6}$/);
11064
+ /** `pair.offer/v1` for a link or a QR: a linked device asks its bridge to open a rendezvous. */
11065
+ const PairLinkOfferOrderSchema = z.strictObject({
11066
+ t: z.literal(TRUST_SIGNATURE_TAGS.pairOffer),
11067
+ /** Which kind of device the link is for: another browser or a phone. */
11068
+ kind: z.enum(["link", "phone"]),
11069
+ /** The rendezvous identifier: 16 random bytes in base64url. */
11070
+ x: z.string().length(22).refine(isCanonicalBase64url),
11071
+ /** The link's secret: 16 random bytes in base64url. Travels inside the encrypted order only. */
11072
+ s: z.string().length(22).refine(isCanonicalBase64url)
11073
+ });
11074
+ /** `pair.offer/v1` for another machine: the short code, drawn by the linked device. */
11075
+ const PairCodeOfferOrderSchema = z.strictObject({
11076
+ t: z.literal(TRUST_SIGNATURE_TAGS.pairOffer),
11077
+ kind: z.literal("machine"),
11078
+ /** The address to meet at (8 symbols). */
11079
+ x: PairCodeIdSchema,
11080
+ /** The password (6 symbols). Travels inside the encrypted order only. */
11081
+ w: PairCodeSecretSchema
11082
+ });
11083
+ z.union([PairLinkOfferOrderSchema, PairCodeOfferOrderSchema]);
8729
11084
  z.strictObject({
8730
11085
  t: z.literal(PAIR_MESSAGE_TYPES.request),
8731
11086
  /** The account of the requester, as the relay knows it. */
8732
11087
  account: RelayIdSchema,
8733
11088
  browser: z.strictObject({
8734
- device: z.string().regex(/^browser:[A-Za-z0-9_-]{16}$/),
11089
+ device: z.string().regex(REQUESTER_DEVICE_ID_PATTERN),
8735
11090
  sig: DeviceSigKeySchema,
8736
11091
  enc: DeviceEncKeySchema
8737
11092
  }),
@@ -8739,7 +11094,29 @@ z.strictObject({
8739
11094
  declared: z.strictObject({
8740
11095
  type: label,
8741
11096
  os: label
8742
- })
11097
+ }),
11098
+ /**
11099
+ * The requester's signature recognising the creator's key as the root of the link (the root
11100
+ * the link carried): it ties that key to the account without any gesture. Optional: a requester
11101
+ * that does not sign it is linked all the same, only the key stays unattached.
11102
+ */
11103
+ root_certification: z.strictObject({
11104
+ issued_at: z.iso.datetime(),
11105
+ sig: Ed25519SignatureSchema
11106
+ }).optional(),
11107
+ /**
11108
+ * Only a machine joining with a short code: the product it serves and the declaration it
11109
+ * signed to be attached to the relay (the bridge that holds the rendezvous vouches for it).
11110
+ */
11111
+ machine: z.strictObject({
11112
+ product: z.string().regex(PRODUCT_ID),
11113
+ relay_device: RelayIdSchema,
11114
+ attach_expires_at: z.number().int().positive(),
11115
+ attach_sig: Ed25519SignatureSchema
11116
+ }).optional()
11117
+ }).refine((request) => request.browser.device.startsWith("machine:") === (request.machine !== void 0), {
11118
+ path: ["machine"],
11119
+ message: "a machine says what it serves, and only a machine does"
8743
11120
  });
8744
11121
  z.strictObject({
8745
11122
  t: z.literal(PAIR_MESSAGE_TYPES.welcome),
@@ -8753,9 +11130,11 @@ z.strictObject({
8753
11130
  enc: z.strictObject({
8754
11131
  alg: z.literal(DEVICE_ENC_ALG),
8755
11132
  key: key32
8756
- })
11133
+ }),
11134
+ /** The identifier the relay knows the bridge by: where the browser's messages to it go. */
11135
+ relay_device: z.string().regex(/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/).optional()
8757
11136
  }),
8758
- browser: z.string().regex(/^browser:[A-Za-z0-9_-]{16}$/),
11137
+ browser: z.string().regex(REQUESTER_DEVICE_ID_PATTERN),
8759
11138
  /** SHA-256 of the opened request, base64url. */
8760
11139
  request: key32,
8761
11140
  registry: z.array(z.strictObject({
@@ -8769,12 +11148,38 @@ z.strictObject({
8769
11148
  })).max(64),
8770
11149
  sig: Ed25519SignatureSchema
8771
11150
  });
11151
+ z.strictObject({
11152
+ t: z.literal(PAIR_MESSAGE_TYPES.hello),
11153
+ root: z.strictObject({
11154
+ device: z.string().regex(/^(machine|human):[A-Za-z0-9_-]{16}$/),
11155
+ key: key32
11156
+ }),
11157
+ bridge: z.strictObject({
11158
+ device: z.string().regex(/^machine:[A-Za-z0-9_-]{16}$/),
11159
+ sig: DeviceSigKeySchema,
11160
+ enc: z.strictObject({
11161
+ alg: z.literal(DEVICE_ENC_ALG),
11162
+ key: key32
11163
+ })
11164
+ }),
11165
+ /** The account of the bridge, which the machine joins. */
11166
+ account: RelayIdSchema,
11167
+ now: z.number().int().positive()
11168
+ });
8772
11169
  z.strictObject({
8773
11170
  t: z.literal(PAIR_MESSAGE_TYPES.productKeys),
8774
- product: z.string().regex(/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/),
11171
+ product: z.string().regex(PRODUCT_ID),
8775
11172
  /** The identifier of the key (`apkeyid1`): the browser checks it against the journal's events. */
8776
11173
  key_id: z.string().min(1).max(64),
8777
- key: key32
11174
+ key: key32,
11175
+ /**
11176
+ * The keys the product had before it was renewed, so that a device linked afterwards reads the
11177
+ * whole history. Each is checked against its own `key_id` by the receiver.
11178
+ */
11179
+ older: z.array(z.strictObject({
11180
+ key_id: z.string().min(1).max(64),
11181
+ key: key32
11182
+ })).max(32).optional()
8778
11183
  });
8779
11184
  z.discriminatedUnion("k", [
8780
11185
  z.strictObject({
@@ -8793,111 +11198,529 @@ z.discriminatedUnion("k", [
8793
11198
  ciphertext: z.string().min(1).max(65536)
8794
11199
  })
8795
11200
  }),
11201
+ z.strictObject({
11202
+ k: z.literal("pake_init"),
11203
+ nonce: key22,
11204
+ share: key32
11205
+ }),
11206
+ z.strictObject({
11207
+ k: z.literal("pake_reply"),
11208
+ share: key32
11209
+ }),
11210
+ z.strictObject({
11211
+ k: z.literal("pake_confirm"),
11212
+ mac: key32
11213
+ }),
11214
+ z.strictObject({
11215
+ k: z.literal("pake_confirm_b"),
11216
+ mac: key32
11217
+ }),
11218
+ z.strictObject({
11219
+ k: z.literal("hello"),
11220
+ nonce: z.string().min(1).max(64),
11221
+ ciphertext: z.string().min(1).max(65536)
11222
+ }),
8796
11223
  z.strictObject({
8797
11224
  k: z.literal("refused"),
8798
11225
  code: z.enum(Object.values(PAIR_ERROR_CODES))
8799
11226
  })
8800
11227
  ]);
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
- ])
11228
+ //#endregion
11229
+ //#region ../../packages/protocol/dist/product-browse.js
11230
+ /**
11231
+ * Choosing where a product is made, by clicking: the shapes the request and the answer of the file
11232
+ * reading carry (`folders`). A device names a place the machine listed in its report and the steps
11233
+ * taken inside it, one folder name each, as the machine listed them; it never sends a path. The
11234
+ * machine resolves the steps itself, refuses whatever leaves the place (a link, a junction, a
11235
+ * separator, `..`), and lists only the folders a creator may put a product in: no hidden folder,
11236
+ * no folder of the system.
11237
+ */
11238
+ /** The request shape (without the identifier, the version and the product, which the file reading adds). */
11239
+ const FoldersRequestFields = {
11240
+ op: z.literal("folders"),
11241
+ /** The identifier of a place the machine listed. */
11242
+ location: z.string().regex(PRODUCT_LOCATION_ID_PATTERN),
11243
+ /** The folders gone into, from the place. */
11244
+ path: z.array(z.string().refine(isBrowseStep, { message: "a folder name, not a path" })).max(12).optional()
11245
+ };
11246
+ const FoldersDataSchema = z.strictObject({
11247
+ op: z.literal("folders"),
11248
+ location: z.string().regex(PRODUCT_LOCATION_ID_PATTERN),
11249
+ /** The steps the answer is about (the request's). */
11250
+ path: z.array(z.string().refine(isBrowseStep)).max(12),
11251
+ /** The folder as the creator reads it (the home folder written `~`). */
11252
+ label: z.string().min(1).max(1200),
11253
+ /** The folders inside it that can be gone into, by name. */
11254
+ folders: z.array(z.string().refine(isBrowseStep)).max(200),
11255
+ /** True when there are more folders than listed. */
11256
+ truncated: z.boolean()
8829
11257
  });
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())
11258
+ //#endregion
11259
+ //#region ../../packages/protocol/dist/voice-read.js
11260
+ /** The largest piece of audio of one request, in bytes of PCM (3 seconds); the envelope must hold it sealed. */
11261
+ const TRANSCRIBE_CHUNK_MAX_BYTES = 96e3;
11262
+ /** The longest text the machine gives back, in characters. */
11263
+ const TRANSCRIBE_TEXT_MAX_CHARS = 4e3;
11264
+ /** What the request carries on top of the file reading's own fields. */
11265
+ const TranscribeRequestFields = {
11266
+ op: z.literal("transcribe"),
11267
+ /**
11268
+ * `probe`: asks whether the machine can transcribe, with no audio. `audio`: one piece of a
11269
+ * phrase; the last one (`final`) makes the machine transcribe and answer with the text.
11270
+ */
11271
+ step: z.enum(["probe", "audio"]),
11272
+ /** The phrase the piece belongs to (audio only). */
11273
+ session: z.string().regex(ULID_PATTERN).optional(),
11274
+ /** The place of the piece in the phrase, from 0 (audio only). */
11275
+ seq: z.int().min(0).max(7).optional(),
11276
+ /** True on the last piece of the phrase (audio only). */
11277
+ final: z.boolean().optional(),
11278
+ /** The language to recognize, a BCP-47 tag (audio only). */
11279
+ language: z.string().regex(/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})?$/).optional(),
11280
+ /** The PCM of the piece, base64url (audio only). */
11281
+ audio: z.string().regex(/^[A-Za-z0-9_-]*$/).max(base64urlLength(TRANSCRIBE_CHUNK_MAX_BYTES)).optional()
11282
+ };
11283
+ const TranscribeDataSchema = z.strictObject({
11284
+ op: z.literal("transcribe"),
11285
+ /** True when the machine has a transcription command set up. */
11286
+ ready: z.boolean(),
11287
+ /** The words understood: only on the answer to the last piece of a phrase. */
11288
+ text: z.string().max(TRANSCRIBE_TEXT_MAX_CHARS).optional()
8836
11289
  });
11290
+ /** The longest path, in characters, and the longest one name of a branch. */
11291
+ const FILES_PATH_MAX = 1024;
8837
11292
  /**
8838
- * Two `checkpoint.opened` events give the same checkpoint id: `ids[0]` opened it, the others are
8839
- * ignored (`checkpoint_id_collision`).
11293
+ * Where a file is read: the working copy of the product as it is now (uncommitted work included),
11294
+ * a branch or a commit of its repository, or the working copy of the story that works on a branch.
8840
11295
  */
8841
- const CheckpointIdCollisionWarningSchema = z.strictObject({
8842
- code: z.literal("checkpoint_id_collision"),
8843
- checkpoint: z.string(),
8844
- ids: z.array(z.string())
11296
+ const FilesRefSchema = z.discriminatedUnion("kind", [
11297
+ z.strictObject({ kind: z.literal("worktree") }),
11298
+ z.strictObject({
11299
+ kind: z.literal("branch"),
11300
+ name: z.string().min(1).max(200)
11301
+ }),
11302
+ z.strictObject({
11303
+ kind: z.literal("commit"),
11304
+ sha: z.string().regex(/^[0-9a-f]{40,64}$/)
11305
+ }),
11306
+ z.strictObject({
11307
+ kind: z.literal("workspace"),
11308
+ branch: z.string().min(1).max(200)
11309
+ })
11310
+ ]);
11311
+ const RequestBase = {
11312
+ v: z.literal(1),
11313
+ /** Chosen by the asking device: the answer repeats it. */
11314
+ id: z.string().regex(ULID_PATTERN),
11315
+ product: z.string().regex(ULID_PATTERN)
11316
+ };
11317
+ z.discriminatedUnion("op", [
11318
+ z.strictObject({
11319
+ ...RequestBase,
11320
+ op: z.literal("tree"),
11321
+ ref: FilesRefSchema,
11322
+ path: z.string().max(FILES_PATH_MAX)
11323
+ }),
11324
+ z.strictObject({
11325
+ ...RequestBase,
11326
+ op: z.literal("file"),
11327
+ ref: FilesRefSchema,
11328
+ path: z.string().min(1).max(FILES_PATH_MAX)
11329
+ }),
11330
+ z.strictObject({
11331
+ ...RequestBase,
11332
+ op: z.literal("download"),
11333
+ ref: FilesRefSchema,
11334
+ path: z.string().min(1).max(FILES_PATH_MAX)
11335
+ }),
11336
+ z.strictObject({
11337
+ ...RequestBase,
11338
+ op: z.literal("refs")
11339
+ }),
11340
+ z.strictObject({
11341
+ ...RequestBase,
11342
+ op: z.literal("changes"),
11343
+ base: FilesRefSchema,
11344
+ head: FilesRefSchema
11345
+ }),
11346
+ z.strictObject({
11347
+ ...RequestBase,
11348
+ op: z.literal("live"),
11349
+ after: z.number().int().min(0).optional(),
11350
+ epoch: z.string().min(1).max(64).optional()
11351
+ }),
11352
+ z.strictObject({
11353
+ ...RequestBase,
11354
+ ...CodePullsRequestFields
11355
+ }),
11356
+ z.strictObject({
11357
+ ...RequestBase,
11358
+ ...CodePullRequestFields
11359
+ }),
11360
+ z.strictObject({
11361
+ ...RequestBase,
11362
+ ...CodeDiffRequestFields
11363
+ }),
11364
+ z.strictObject({
11365
+ ...RequestBase,
11366
+ ...CodeEditorRequestFields
11367
+ }),
11368
+ z.strictObject({
11369
+ ...RequestBase,
11370
+ ...DocsListRequestFields
11371
+ }),
11372
+ z.strictObject({
11373
+ ...RequestBase,
11374
+ ...DocsTraceRequestFields
11375
+ }),
11376
+ z.strictObject({
11377
+ ...RequestBase,
11378
+ ...DocsRetrosRequestFields
11379
+ }),
11380
+ z.strictObject({
11381
+ ...RequestBase,
11382
+ ...KbListRequestFields
11383
+ }),
11384
+ z.strictObject({
11385
+ ...RequestBase,
11386
+ ...KbAskRequestFields
11387
+ }),
11388
+ z.strictObject({
11389
+ ...RequestBase,
11390
+ ...CostsRequestFields
11391
+ }),
11392
+ z.strictObject({
11393
+ ...RequestBase,
11394
+ ...BriefingRequestFields
11395
+ }),
11396
+ z.strictObject({
11397
+ ...RequestBase,
11398
+ ...TranscribeRequestFields
11399
+ }),
11400
+ z.strictObject({
11401
+ ...RequestBase,
11402
+ ...FindRequestFields
11403
+ }),
11404
+ z.strictObject({
11405
+ ...RequestBase,
11406
+ ...FoldersRequestFields
11407
+ })
11408
+ ]);
11409
+ /** Why a request was not answered with data; stable codes the views translate. */
11410
+ const FilesErrorCodeSchema = z.enum([
11411
+ "NOT_FOUND",
11412
+ "OUTSIDE_PRODUCT",
11413
+ "PROTECTED",
11414
+ "TOO_LARGE",
11415
+ "WRONG_KIND",
11416
+ "BAD_REQUEST",
11417
+ "UNAVAILABLE",
11418
+ "BUSY",
11419
+ ...CODE_ERROR_CODES
11420
+ ]);
11421
+ const FilesEntrySchema = z.strictObject({
11422
+ name: z.string().min(1).max(255),
11423
+ kind: z.enum([
11424
+ "file",
11425
+ "dir",
11426
+ "link"
11427
+ ]),
11428
+ /** Bytes; absent for a folder. */
11429
+ size: z.number().int().min(0).optional(),
11430
+ /** True for a file the bridge never sends (secrets): the name is visible, the content is not. */
11431
+ protected: z.literal(true).optional()
11432
+ });
11433
+ const FilesBranchSchema = z.strictObject({
11434
+ name: z.string().min(1).max(200),
11435
+ sha: z.string().regex(/^[0-9a-f]{40,64}$/),
11436
+ /** True for the branch the product's folder is on now. */
11437
+ current: z.literal(true).optional(),
11438
+ /** True when a working copy of this branch exists on the machine (a story at work). */
11439
+ workspace: z.literal(true).optional()
11440
+ });
11441
+ const FilesChangeSchema = z.strictObject({
11442
+ status: z.enum([
11443
+ "added",
11444
+ "modified",
11445
+ "deleted",
11446
+ "renamed"
11447
+ ]),
11448
+ path: z.string().min(1).max(FILES_PATH_MAX),
11449
+ /** The old path of a renamed file. */
11450
+ from: z.string().min(1).max(FILES_PATH_MAX).optional(),
11451
+ protected: z.literal(true).optional()
8845
11452
  });
8846
11453
  /**
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"]))
11454
+ * One line of what an agent does. `kind` is a stable code; `text` is plain text the view never
11455
+ * interprets: what the agent said (`say`), the name of a tool it called (`tool`, `tool_ok`,
11456
+ * `tool_failed`, `denied`) or the outcome code of the run (`end`).
11457
+ *
11458
+ * Three kinds tell what a call does to the machine: `read` and `edit` carry the path of the file,
11459
+ * relative to the agent's working folder, and `command` carries the command; a command's issue
11460
+ * (`command_ok`, `command_failed`) has no text and points to its command by `of`. A path that
11461
+ * leaves the working folder is never told: the line has `outside` and no text. `protected` marks
11462
+ * a file whose content the bridge never shows. `link` says where to read what an `edit` changed:
11463
+ * the same references and paths as the file reading (`file`, `changes`). The results of a tool,
11464
+ * and the content of files, are never in a line.
11465
+ *
11466
+ * In a conversation with Noah two more kinds appear: `creator`, the creator's own message (plain
11467
+ * text, so that their other devices see it), and `routed`, what the bridge did with a handover
11468
+ * Noah recorded, as a stable code (`noah:started?agent=iris`) the view tells in the creator's
11469
+ * language. What Noah says is a `say` of the agent `noah`: untrusted text, like any agent's.
11470
+ */
11471
+ const LiveLineSchema = z.strictObject({
11472
+ seq: z.number().int().min(1),
11473
+ /** Milliseconds since the epoch, on the bridge's clock. */
11474
+ at: z.number().int().min(0),
11475
+ agent: z.string().min(1).max(64),
11476
+ kind: z.enum([
11477
+ "start",
11478
+ "say",
11479
+ "tool",
11480
+ "tool_ok",
11481
+ "tool_failed",
11482
+ "denied",
11483
+ "end",
11484
+ "read",
11485
+ "edit",
11486
+ "command",
11487
+ "command_ok",
11488
+ "command_failed",
11489
+ "creator",
11490
+ "routed"
11491
+ ]),
11492
+ text: z.string().max(2e3).optional(),
11493
+ /** For a command's issue: the `seq` of the line of the command. */
11494
+ of: z.number().int().min(1).optional(),
11495
+ /** The path leaves the agent's working folder: it is not told. */
11496
+ outside: z.literal(true).optional(),
11497
+ /** The file is a secret's: its name is told, its content is never shown. */
11498
+ protected: z.literal(true).optional(),
11499
+ /** The line comes from a session the creator opened in the tool's own terminal. */
11500
+ terminal: z.literal(true).optional(),
11501
+ /** For a change: where its content and its difference can be read. */
11502
+ link: z.strictObject({
11503
+ ref: FilesRefSchema,
11504
+ path: z.string().min(1).max(FILES_PATH_MAX)
11505
+ }).optional()
8855
11506
  });
8856
11507
  /**
8857
- * A `high` risk checkpoint answered with the `locale` assurance only (no `presence` credential):
8858
- * accepted, and shown as such.
11508
+ * Whether the sessions the creator opens in a tool's own terminal can be followed: `listening`
11509
+ * (the tool's hook is in the repository), `hook_missing` (it is not: `sync` writes it),
11510
+ * `unsupported` (the tool has no such hook yet). A stable code the views tell in the creator's language.
8859
11511
  */
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
- });
11512
+ const LiveTerminalStateSchema = z.enum([
11513
+ "listening",
11514
+ "hook_missing",
11515
+ "unsupported"
11516
+ ]);
11517
+ /** Where the chain of the product stands, as a code the views tell in the creator's language. */
11518
+ const LiveChainStateSchema = z.enum([
11519
+ "none",
11520
+ "running",
11521
+ "waiting",
11522
+ "paused",
11523
+ "handed_over",
11524
+ "conversation",
11525
+ "stopped"
11526
+ ]);
11527
+ /** What an answer carries, by operation. */
11528
+ const FilesDataSchema = z.discriminatedUnion("op", [
11529
+ CodePullsDataSchema,
11530
+ CodePullDataSchema,
11531
+ CodeDiffDataSchema,
11532
+ CodeEditorDataSchema,
11533
+ DocsListDataSchema,
11534
+ DocsTraceDataSchema,
11535
+ DocsRetrosDataSchema,
11536
+ KbListDataSchema,
11537
+ KbAskDataSchema,
11538
+ FindDataSchema,
11539
+ CostsDataSchema,
11540
+ BriefingDataSchema,
11541
+ TranscribeDataSchema,
11542
+ FoldersDataSchema,
11543
+ z.strictObject({
11544
+ op: z.literal("tree"),
11545
+ path: z.string().max(FILES_PATH_MAX),
11546
+ entries: z.array(FilesEntrySchema).max(5e3),
11547
+ /** True when the folder holds more than the bridge lists. */
11548
+ truncated: z.boolean()
11549
+ }),
11550
+ z.strictObject({
11551
+ op: z.literal("file"),
11552
+ path: z.string().min(1).max(FILES_PATH_MAX),
11553
+ size: z.number().int().min(0),
11554
+ /** `text`: shown; `binary`: only offered for download. */
11555
+ content: z.enum(["text", "binary"]),
11556
+ /** True when the file can be asked for as bytes to save (it is not larger than the bridge sends). */
11557
+ downloadable: z.boolean(),
11558
+ /** The text, with each line that looked like a secret replaced by `redacted:secret`. */
11559
+ text: z.string().optional(),
11560
+ /** How many lines were replaced. */
11561
+ masked: z.number().int().min(0)
11562
+ }),
11563
+ z.strictObject({
11564
+ op: z.literal("download"),
11565
+ path: z.string().min(1).max(FILES_PATH_MAX),
11566
+ size: z.number().int().min(0),
11567
+ /** The bytes, base64url. */
11568
+ bytes: z.string()
11569
+ }),
11570
+ z.strictObject({
11571
+ op: z.literal("refs"),
11572
+ branches: z.array(FilesBranchSchema).max(500),
11573
+ truncated: z.boolean()
11574
+ }),
11575
+ z.strictObject({
11576
+ op: z.literal("changes"),
11577
+ files: z.array(FilesChangeSchema).max(5e3),
11578
+ truncated: z.boolean()
11579
+ }),
11580
+ z.strictObject({
11581
+ op: z.literal("live"),
11582
+ /** Tells one run of the bridge's memory from another: send it back with `next`. */
11583
+ epoch: z.string().min(1).max(64),
11584
+ /** True when the cursor asked with was not this bridge's: the lines start again from the oldest kept. */
11585
+ reset: z.boolean(),
11586
+ /** How many lines were lost between the cursor and the first line below (memory is bounded). */
11587
+ dropped: z.number().int().min(0),
11588
+ /** The cursor to ask with next time. */
11589
+ next: z.number().int().min(0),
11590
+ /** True when more lines wait: ask again at once. */
11591
+ more: z.boolean(),
11592
+ chain: LiveChainStateSchema,
11593
+ /** The agent that works now, or null. */
11594
+ working: z.strictObject({
11595
+ agent: z.string().min(1).max(64),
11596
+ story: z.string().min(1).max(32).optional(),
11597
+ tool: z.string().min(1).max(32)
11598
+ }).nullable(),
11599
+ lines: z.array(LiveLineSchema).max(100),
11600
+ /** For each tool, whether its sessions opened in the creator's own terminal can be followed. */
11601
+ terminal: z.array(z.strictObject({
11602
+ tool: z.enum([
11603
+ "claude-code",
11604
+ "codex",
11605
+ "antigravity",
11606
+ "cursor"
11607
+ ]),
11608
+ state: LiveTerminalStateSchema
11609
+ })).max(8).optional(),
11610
+ /** Present while the creator holds the session: where to go and what to type in the terminal. */
11611
+ handover: z.strictObject({
11612
+ tool: z.string().min(1).max(32),
11613
+ cwd: z.string().min(1).max(FILES_PATH_MAX),
11614
+ command: z.string().min(1).max(300)
11615
+ }).optional()
11616
+ })
11617
+ ]);
11618
+ const FilesResponseSchema = z.discriminatedUnion("ok", [z.strictObject({
11619
+ v: z.literal(1),
11620
+ id: z.string().regex(ULID_PATTERN),
11621
+ product: z.string().regex(ULID_PATTERN),
11622
+ ok: z.literal(true),
11623
+ /** The commit the answer was read at, or `worktree` for the working copy. */
11624
+ at: z.string().max(80),
11625
+ /** Milliseconds since the epoch, on the bridge's clock: when it was read. */
11626
+ read_at: z.number().int().min(0),
11627
+ data: FilesDataSchema
11628
+ }), z.strictObject({
11629
+ v: z.literal(1),
11630
+ id: z.string().regex(ULID_PATTERN),
11631
+ product: z.string().regex(ULID_PATTERN),
11632
+ ok: z.literal(false),
11633
+ code: FilesErrorCodeSchema,
11634
+ /** For the platform codes: the host of the repository, so that the device can name it. */
11635
+ host: ForgeHostSchema.optional(),
11636
+ /** For `TOO_LARGE`: the size of the file and what the bridge shows at most. */
11637
+ size: z.number().int().min(0).optional(),
11638
+ max: z.number().int().min(0).optional()
11639
+ })]);
11640
+ readTolerant(FilesDataSchema);
11641
+ readTolerant(FilesResponseSchema);
11642
+ //#endregion
11643
+ //#region ../../packages/protocol/dist/share.js
8869
11644
  /**
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`.
11645
+ * The read-only progress page: a page a creator shares without any account. The device builds it
11646
+ * from a short allow-list (never a copy of the state), seals it with a key made for this one link,
11647
+ * and hands the relay the sealed bytes only. The key travels in the fragment of the link, which no
11648
+ * browser sends to a server, so the relay can neither read the page nor give it to anyone who does
11649
+ * not hold the whole link. The relay erases the page at its expiry or when the creator withdraws
11650
+ * the link.
8873
11651
  */
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()
11652
+ /**
11653
+ * The stages and the phases a page can name. Written here, not imported, so that the page that
11654
+ * reads a link stays light (a test compares them with the lists of the product and of the method).
11655
+ */
11656
+ const SHARE_STAGES = [
11657
+ "framing",
11658
+ "development",
11659
+ "launch",
11660
+ "online"
11661
+ ];
11662
+ const SHARE_PHASES = [
11663
+ "framing",
11664
+ "planning",
11665
+ "design",
11666
+ "build",
11667
+ "delivery",
11668
+ "launch"
11669
+ ];
11670
+ /** The largest sealed page, in bytes. */
11671
+ const SHARE_MAX_SEALED_BYTES = 65536;
11672
+ /** The identifier of a link: 16 random bytes, in base64url (22 characters). */
11673
+ const SHARE_ID_PATTERN = /^[A-Za-z0-9_-]{22}$/;
11674
+ const sealedText = (maxBytes) => z.string().min(1).max(Math.ceil(maxBytes * 4 / 3)).refine(isCanonicalBase64url);
11675
+ z.strictObject({
11676
+ product: z.string().regex(/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/),
11677
+ /** 12 bytes. */
11678
+ nonce: z.string().length(16).refine(isCanonicalBase64url),
11679
+ ciphertext: sealedText(SHARE_MAX_SEALED_BYTES)
8883
11680
  });
11681
+ z.strictObject({ id: z.string().regex(SHARE_ID_PATTERN) });
8884
11682
  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())
11683
+ v: z.literal(1),
11684
+ /** When the page was made, in milliseconds since the epoch. */
11685
+ made_at: z.number().int().nonnegative(),
11686
+ product: z.strictObject({
11687
+ name: z.string().min(1).max(80),
11688
+ stage: z.enum(SHARE_STAGES)
11689
+ }),
11690
+ phases: z.array(z.strictObject({
11691
+ id: z.enum(SHARE_PHASES),
11692
+ state: z.enum([
11693
+ "done",
11694
+ "current",
11695
+ "review",
11696
+ "upcoming"
11697
+ ])
11698
+ })).max(SHARE_PHASES.length),
11699
+ sprint: z.strictObject({
11700
+ goal: z.string().min(1).max(160).nullable(),
11701
+ done: z.number().int().nonnegative(),
11702
+ total: z.number().int().nonnegative()
11703
+ }).nullable(),
11704
+ /** Stories merged, all sprints together. */
11705
+ delivered: z.number().int().nonnegative(),
11706
+ stories: z.array(z.strictObject({
11707
+ id: z.string().regex(/^[A-Za-z0-9.-]{1,16}$/),
11708
+ /** `null` when the title was held back or the story has none. */
11709
+ title: z.string().min(1).max(120).nullable(),
11710
+ state: z.enum([
11711
+ "todo",
11712
+ "development",
11713
+ "review",
11714
+ "done"
11715
+ ]),
11716
+ /** Steps (acceptance criteria) proven out of those the story lists; `null` when none is listed. */
11717
+ steps: z.strictObject({
11718
+ done: z.number().int().nonnegative(),
11719
+ total: z.number().int().nonnegative()
11720
+ }).nullable()
11721
+ })).max(60),
11722
+ /** Stories of the sprint past the limit, counted and not listed. */
11723
+ more: z.number().int().nonnegative()
8901
11724
  });
8902
11725
  /** A page of checkpoints, newest first: filtered by status, type or story, before an id. */
8903
11726
  const StateCheckpointsInputSchema = z.strictObject({
@@ -8985,6 +11808,87 @@ const CursorCliConfigSchema = z.looseObject({ permissions: z.looseObject({
8985
11808
  allow: z.array(z.string()).optional(),
8986
11809
  deny: z.array(z.string()).optional()
8987
11810
  }).optional() });
11811
+ /** The CSV export's columns: those of `HistoryEntry`, then the reason of an ignored answer. */
11812
+ const HISTORY_CSV_COLUMNS = [...[
11813
+ "at",
11814
+ "author",
11815
+ "author_trust",
11816
+ "author_endorsed_at",
11817
+ "source",
11818
+ "decision",
11819
+ "option",
11820
+ "waiver_until",
11821
+ "assurance",
11822
+ "locale_on_high_risk",
11823
+ "checkpoint",
11824
+ "checkpoint_type",
11825
+ "checkpoint_risk",
11826
+ "checkpoint_title",
11827
+ "story",
11828
+ "event",
11829
+ "checkpoint_phase",
11830
+ "quote",
11831
+ "relayed_by",
11832
+ "channel",
11833
+ "stack",
11834
+ "via"
11835
+ ], "ignored_reason"];
11836
+ /** The UTF-8 byte order mark: spreadsheets then read the accents right. */
11837
+ const BOM = String.fromCharCode(65279);
11838
+ /** Starts a spreadsheet formula when a cell begins with it. */
11839
+ const FORMULA_START = /^[=+\-@\t\r]/;
11840
+ /**
11841
+ * One CSV field (RFC 4180): quoted when it holds a comma, a quote or a line break. A text a
11842
+ * spreadsheet would run as a formula (`=`, `+`, `-`, `@`, tab, CR first) is prefixed with `'`.
11843
+ */
11844
+ function csvField(value) {
11845
+ let text = value === null ? "" : String(value);
11846
+ if (FORMULA_START.test(text)) text = `'${text}`;
11847
+ return /[",\r\n]/.test(text) ? `"${text.replaceAll("\"", "\"\"")}"` : text;
11848
+ }
11849
+ function ignoredRow(item) {
11850
+ return {
11851
+ at: item.at,
11852
+ author: item.author,
11853
+ author_trust: null,
11854
+ author_endorsed_at: null,
11855
+ source: null,
11856
+ decision: item.decision,
11857
+ option: null,
11858
+ waiver_until: null,
11859
+ assurance: null,
11860
+ locale_on_high_risk: null,
11861
+ checkpoint: item.checkpoint,
11862
+ checkpoint_type: null,
11863
+ checkpoint_risk: null,
11864
+ checkpoint_title: null,
11865
+ story: item.story,
11866
+ checkpoint_phase: null,
11867
+ quote: null,
11868
+ relayed_by: null,
11869
+ channel: null,
11870
+ stack: null,
11871
+ via: null,
11872
+ event: item.event,
11873
+ ignored_reason: item.reason
11874
+ };
11875
+ }
11876
+ /**
11877
+ * The CSV export, for spreadsheets (RFC 4180, UTF-8 with a byte order mark so that accents read
11878
+ * right): a header of the English field names, then one record per entry, then (`--all`) one per
11879
+ * ignored answer with its `ignored_reason` (empty for the valid ones). Each line ends with CRLF; a
11880
+ * null is an empty field; formulas are neutralised (`csvField`), so the JSON export is the exact
11881
+ * one.
11882
+ */
11883
+ function historyCsv(entries, ignored = null) {
11884
+ const rows = [...entries.map((entry) => ({
11885
+ ...entry,
11886
+ ignored_reason: null
11887
+ })), ...(ignored ?? []).map(ignoredRow)];
11888
+ const lines = [HISTORY_CSV_COLUMNS.join(",")];
11889
+ for (const row of rows) lines.push(HISTORY_CSV_COLUMNS.map((column) => csvField(row[column])).join(","));
11890
+ return `${BOM}${lines.map((line) => `${line}\r\n`).join("")}`;
11891
+ }
8988
11892
  //#endregion
8989
11893
  //#region ../../packages/crypto/dist/encoding.js
8990
11894
  const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
@@ -9790,9 +12694,4 @@ function gitBlobId(bytes, algorithm = "sha1") {
9790
12694
  return toHex(algorithm === "sha1" ? sha1(object) : sha256$1(object));
9791
12695
  }
9792
12696
  //#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 };
12697
+ export { readinessIdOf as $, UpdateStoryInputSchema as $a, lessonIdOf as $i, CI_EVENT_TYPES as $n, criterionId as $r, RepositoryPathSchema as $t, RoomIdSchema as A, SkillContentSchema as Aa, ReportMeasuresInputSchema as Ai, COURSE_EVENT_TYPES as An, MODEL_TIERS as Ao, CHAT_RETENTION_MS as Ar, JOURNAL_REF as At, OpenStackInputSchema as B, DeferredReasonSchema as Ba, hasMisleadingControl as Bi, DocumentHeaderSchema as Bn, recommendedGitWorkflow as Bo, menuKey as Br, FAST_TRACK_ENGINE_REASONS as Bt, USAGE_EVENT_TYPES as C, PrerequisiteCommandSchema as Ca, CONFIRMATION_MODES as Ci, SOLO_METHODS as Cn, TestSettingsSchema as Co, isTrustSignatureTag as Cr, MACHINE_EVENT_TYPES as Ct, TEST_CRITERIA as D, FORM_CONTENT_MAX_CHARS as Da, OpenCheckpointInputSchema as Di, hypothesisIdOf as Dn, INHERIT_MODEL as Do, AnswerCheckpointInputSchema as Dr, LoopInputSchema as Dt, ReviewTestsInputSchema as E, AgentContentSchema as Ea, NOTE_REQUIRED as Ei, critiqueStepOf as En, ValidationSettingsSchema as Eo, ANTICIPATED_ANSWER_WINDOW_MS as Er, LOOP_EVENT_TYPES as Et, THREE_VIEWS_AGENTS as F, plainLine as Fa, SESSION_EVENT_TYPES as Fi, ArchitectureHeaderSchema as Fn, GIT_CI_MODES as Fo, QuestionStackProofSchema as Fr, journalSeenRef as Ft, PlanSprintInputSchema as G, AGENT_EVENT_TYPES as Ga, PROMOTION_TITLE as Gi, REQUIREMENT_ID_PATTERN as Gn, SIGNATURE_REASON as Go, QA_COMMAND as Gr, missingDeclarations as Gt, stackIdOf as H, compareStoryIds as Ha, LESSON_EVENT_TYPES as Hi, EpicHeaderSchema as Hn, sameWorkflow as Ho, SkillFrontmatterSchema as Hr, FAST_TRACK_OPTIONS as Ht, flatTable as I, CompleteDeferredInputSchema as Ia, PUBLICATION_DOCS_DIR as Ii, CREATOR_OWNER as In, GIT_EVENT_TYPES as Io, QuestionToolViaSchema as Ir, HUMAN_DEVICE_PATTERN as It, sprintIdOf as J, ReportCostInputSchema as Ja, ProposeRetrospectiveInputSchema as Ji, StoryHeaderSchema as Jn, jsonValueIssue as Jo, qaProofBelongs as Jr, KNOWLEDGE_STORY_FIELD as Jt, SPRINT_EVENT_TYPES as K, MAIN_SESSION_AGENT as Ka, PromoteLessonInputSchema as Ki, STORY_DOCUMENT_ID_PATTERN as Kn, ULID_PATTERN as Ko, QA_EVENT_TYPES as Kr, DEFAULT_MERGE_METHOD as Kt, roundTableStepOf as L, DEFERRED_EVENT_TYPES as La, PUBLICATION_EXPORT_DIR as Li, DOCUMENT_DIRS as Ln, GIT_MERGE_MODES as Lo, RULE_LOCKS as Lr, HUMAN_EVENT_TYPES as Lt, RoundTableInputSchema as M, contentSize as Ma, CheckActionInputSchema as Mi, CourseInputSchema as Mn, NO_EFFORT as Mo, QUESTION_TOOL_NAME as Mr, RESERVED_EVENT_TYPES as Mt, TABLE_ANGLES as N, isSkillContent as Na, RecordExternalReadInputSchema as Ni, courseIdOf as Nn, ISSUE as No, QUESTION_TOOL_VIA as Nr, journalEventPath as Nt, TEST_REVIEW_EVENT_TYPES as O, REQUIRED_CONTENT_FIELDS as Oa, RECIPIENT_ROLES as Oi, isCritiqueGesture as On, LEGACY_GEMINI_CLIENT as Oo, CHAT_DECISIONS as Or, EVENT_TYPES as Ot, TABLE_EVENT_TYPES as P, isSkillOverrideContent as Pa, RequestActionInputSchema as Pi, parseCourseStart as Pn, issueName as Po, QuestionProofSchema as Pr, journalRemoteRef as Pt, ReadyInputSchema as Q, SubmitResultInputSchema as Qa, commonIdOf as Qi, INSTALL_EVENT_TYPES as Qn, criteriaInLine as Qr, MERGE_EVENT_TYPES as Qt, AnswerStackInputSchema as R, DeferWorkInputSchema as Ra, countWords as Ri, DOCUMENT_ID_PATTERN as Rn, GitBranchSchema as Ro, AgentDefinitionSchema as Rr, HUMAN_ONLY_EVENT_TYPES as Rt, MeasureUsageInputSchema as S, EnvironmentCheckIdSchema as Sa, CHECKPOINT_EVENT_TYPES as Si, RECONCILE_KINDS as Sn, TOOL_IDS as So, deviceRevokedRecord as Sr, SUBSTANTIVE_CLASSES as St, UsageMeasuredPayloadSchema as T, PrerequisitesSchema as Ta, LIFTING_DECISIONS as Ti, critiquePathOf as Tn, VALIDATION_POINTS as To, rootCertifiedRecord as Tr, HYPOTHESIS_KIND as Tt, MAX_SPRINT_CHECKPOINT_SUMMARY as U, deferredIdOf as Ua, MEMORY_APPROVING_DECISIONS as Ui, KNOWLEDGE_SOURCES_FIELD as Un, EVENT_REASON as Uo, AcceptanceQaInputSchema as Ur, LeaveFastTrackInputSchema as Ut, STACK_EVENT_TYPES as V, canonicalStoryId as Va, CommonLessonIdSchema as Vi, EPIC_DOCUMENT_ID_PATTERN as Vn, resolveGitWorkflow as Vo, AgentFrontmatterSchema as Vr, FAST_TRACK_EVENT_TYPES as Vt, MAX_SPRINT_USD as W, hasControlCharacter as Wa, MEMORY_EVENT_TYPES as Wi, KnowledgeSourceSchema as Wn, EVENT_TYPE_PATTERN as Wo, MAX_QA_PROOF_BYTES as Wr, StartStoryFastTrackInputSchema as Wt, READINESS_FIX_FIRST as X, ReportStatusInputSchema as Xa, RecordLessonsInputSchema as Xi, ARCHITECTURE_DEPENDENCIES_FIELD as Xn, TEST_EVENT_TYPE as Xo, CRITERION_EVENT_TYPES as Xr, KnowledgeStorySchema as Xt, READINESS_EVENT_TYPES as Y, ReportProgressInputSchema as Ya, ReadMemoryInputSchema as Yi, epicIdOf as Yn, parseEventEnvelope as Yo, qaProofKind as Yr, KnowledgeCodeSchema as Yt, READINESS_GO_ON as Z, StoryIdSchema as Za, actionIdOf as Zi, DependenciesSchema as Zn, ErrorCode as Zo, RecordCriterionInputSchema as Zr, MAX_MERGED_FILES as Zt, currentEvent as _, RunProofCommandInputSchema as _a, reviewIdOf as _i, NOAH_AGENT as _n, RULE_VALUES as _o, DEVICE_TRUST_SYSTEM_EVENT_TYPES as _r, CONFLICT_CLASSES as _t, fromBase64url as a, CreatorActionSchema as aa, JUDGE_LENS as ai, DEVIATION_EVENT_TYPES as an, DEVELOPER_SPECIALTIES as ao, MAX_CI_CHECKS as ar, PRODUCT_KEYS_EXPORT_PREFIX as at, isSensitiveEventType as b, isStoryStep as ba, STORY_EVENT_TYPES as bi, CritiqueInputSchema as bn, SkillNameSchema as bo, deviceKindOf as br, MERGE_ORDER_EVENT_TYPES as bt, toHex as c, RequestCreatorActionInputSchema as ca, REVIEW_LENSES as ci, GetTraceInputSchema as cn, InstallSettingsSchema as co, CHECKPOINT_CLOSED_EVENT_TYPES as cr, ProductKeyRotatedPayloadSchema as ct, ClaudeSettingsSchema as d, CheckWriteInputSchema as da, StoryReviewInputSchema as di, ResolveDeviationInputSchema as dn, PHASE_IDS as do, BUDGET_TITLE_PREFIX as dr, WriteSensitiveInputSchema as dt, retroIdOf as ea, REQUIREMENT_EVENT_TYPES as ei, AskCreatorInputSchema as en, AgentIdSchema as eo, CI_FORGES as er, readinessStepOf as et, CursorCliConfigSchema as f, WRITE_GUARD_EVENT_TYPES as fa, SubmitReviewInputSchema as fi, TRACED_REQUIREMENT_PATTERN as fn, PROFILE_PRESET_IDS as fo, WEB_ANSWER_EVENT_TYPES as fr, isSensitivePath as ft, JOURNAL_PROJECTED_REF as g, ProofsFileSchema as ga, isSilentLens as gi, DelegateInputSchema as gn, RULE_ACTIONS as go, DEVICE_TRUST_EVENT_TYPES as gr, MigrationStepIdSchema as gt, REDACTED_SECRET as h, SkillOverrideSchema as ha, isSilentJudge as hi, DELEGATION_EVENT_TYPES as hn, PullRequestSettingsSchema as ho, BRIDGE_ID_PATTERN as hr, METHOD_EVENT_TYPES as ht, sha256Hex as i, CREATOR_ACTION_KINDS as ia, SyncRequirementsInputSchema as ii, MAX_ELICITED_NOTE_LENGTH as in, DEFAULT_VALIDATION_POINTS as io, CiWaiverReasonSchema as ir, PAYLOAD_ENCRYPTION_ALG as it, RoomSchema as j, contentSections as ja, checkpointIdOf as ji, COURSE_KINDS as jn, ModelTableSchema as jo, MessageDigestSchema as jr, MACHINE_DEVICE_PATTERN as jt, TEST_ROWS as k, SKILL_OVERRIDE_FIELDS as ka, RESOLUTION_DECISIONS as ki, isHypothesisOutcome as kn, MODEL_CLIENT_IDS as ko, CHAT_EVENT_TYPES as kr, JOURNAL_DIR as kt, utf8 as l, engineUpdateRisk as la, SECURITY_REASONS as li, REQUIREMENT_STATUSES as ln, MethodConfigSchema as lo, CHAIN_EVENT_TYPES as lr, ReadSensitiveInputSchema as lt, GetStateInputSchema as m, REVIEW_SKILLS as ma, findingIdOf as mi, deviationIdOf as mn, ProfileSchema as mo, WEB_ANSWER_WINDOW_MS as mr, ORDER_EVENT_TYPES as mt, randomBytes as n, scopeLabel as na, SettleGapInputSchema as ni, ELICITATION_TIMEOUT_MS as nn, DEFAULT_PERMISSION_MODE as no, CiReportedPayloadSchema as nr, KEY_ID_PATTERN as nt, fromUtf8 as o, FILE_CREATOR_ACTIONS as oa, REVIEW_DOMAINS as oi, DeviationIdInputSchema as on, DOMAIN_IDS as oo, CHECKPOINT_DECIDED_EVENT_TYPES as or, PRODUCT_KEY_EVENT_TYPES as ot, SyncManifestSchema as p, WRITE_TEXT_MAX as pa, TriageReviewInputSchema as pi, TracedRequirementInputSchema as pn, PROFILE_PRESET_SPECS as po, WEB_ANSWER_STALE_MS as pr, utf8Length as pt, SPRINT_TITLE_PREFIX as q, NextInputSchema as qa, ProposePromotionInputSchema as qi, STORY_SECTIONS as qn, eventInvalid as qo, QA_PROOF_DIR as qr, KNOWLEDGE_CODE_FIELD as qt, sha256 as r, CREATOR_ACTION_EVENT_TYPES as ra, SubmitAuditInputSchema as ri, ElicitationChannelSchema as rn, DEFAULT_VALIDATION_MODE as ro, CiUrlSchema as rr, MAX_SENSITIVE_CONTENT_BYTES as rt, toBase64url as s, FORGE_CREATOR_ACTIONS as sa, REVIEW_EVENT_TYPES as si, DeviationReasonSchema as sn, INSTALL_MODES as so, DecideCheckpointInputSchema as sr, PRODUCT_KEY_EXPORT_PREFIX as st, gitBlobId as t, retroTitle as ta, RecordProofInputSchema as ti, ELICITATION_EVENT_TYPES as tn, DEFAULT_INSTALL_MODE as to, CheckCiInputSchema as tr, ENCRYPTED_FILE_PREFIX as tt, historyCsv as u, proofCommandsRisk as ua, StartReviewInputSchema as ui, ReportDeviationInputSchema as un, PERMISSION_MODES as uo, BUDGET_EVENT_TYPES as ur, SENSITIVE_AREAS as ut, eventTypeSpec as v, normalizedFolder as va, CommitIdSchema as vi, CRITIQUE_DOCUMENT_KINDS as vn, RulesSchema as vo, RootPinSchema as vr, CONFLICT_OPTIONS as vt, USAGE_SOURCES as w, PrerequisiteVersionSchema as wa, CheckpointIdSchema as wi, critiqueIdOf as wn, VALIDATION_MODES as wo, pairOfferRecord as wr, HYPOTHESIS_CONFIDENCES as wt, isTestEventType as x, ARCHITECTURE_PREREQUISITES_FIELD as xa, isStoryBranch as xi, IDEA_VERSION as xn, TEST_FILE_MAX_LINES as xo, devicePairedRecord as xr, QualifyMergeConflictInputSchema as xt, eventTypeUpgrade as y, PHASE_STEPS as ya, OpenPullRequestInputSchema as yi, CRITIQUE_EVENT_TYPES as yn, SemverSchema as yo, TRUST_SIGNATURE_TAGS as yr, GetMergeOrderInputSchema as yt, AskStackInputSchema as z, DeferredIdInputSchema as za, hasHiddenCharacter as zi, DOCUMENT_KINDS as zn, GitWorkflowSchema as zo, AgentOverrideSchema as zr, PresenceCredentialSchema as zt };