agentplugged 1.0.6 → 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.
- package/README.md +3 -4
- package/dist/agent-commands.js +6446 -499
- package/dist/forge-follow.js +3 -3
- package/dist/{webauthn.js → hash.js} +3463 -564
- package/dist/helpers.js +723 -294
- package/dist/launcher.js +1 -1
- package/dist/main.js +336 -40
- package/dist/method/models.yaml +0 -11
- package/dist/method/skills/workshop/references/create.md +1 -1
- package/dist/product-root.js +88 -116
- package/dist/rolldown-runtime.js +1 -1
- package/dist/webauthn-verify.js +112 -1
- package/dist/webauthn-verify2.js +1 -107
- package/package.json +10 -10
|
@@ -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
|
|
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":
|
|
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":
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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/
|
|
4956
|
-
/**
|
|
4957
|
-
*
|
|
4958
|
-
*
|
|
4959
|
-
*
|
|
4960
|
-
*
|
|
4961
|
-
*
|
|
4962
|
-
*
|
|
4963
|
-
*
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
/** The
|
|
4968
|
-
const
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
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
|
-
*
|
|
4980
|
-
*
|
|
4981
|
-
*
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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/
|
|
5473
|
+
//#region ../../packages/protocol/dist/device-trust.js
|
|
5220
5474
|
/**
|
|
5221
|
-
*
|
|
5222
|
-
*
|
|
5223
|
-
*
|
|
5224
|
-
*
|
|
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
|
|
5227
|
-
const
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
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
|
-
*
|
|
5250
|
-
*
|
|
5251
|
-
*
|
|
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
|
-
|
|
5257
|
-
|
|
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
|
-
*
|
|
5260
|
-
*
|
|
5261
|
-
*
|
|
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
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
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
|
-
*
|
|
5275
|
-
* `
|
|
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
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
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/
|
|
7218
|
+
//#region ../../packages/protocol/dist/trace.js
|
|
6014
7219
|
/**
|
|
6015
|
-
*
|
|
6016
|
-
*
|
|
6017
|
-
*
|
|
6018
|
-
*
|
|
6019
|
-
*
|
|
6020
|
-
*
|
|
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
|
|
6025
|
-
const
|
|
6026
|
-
|
|
6027
|
-
|
|
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
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
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
|
-
/**
|
|
6051
|
-
const
|
|
6052
|
-
/**
|
|
6053
|
-
const
|
|
6054
|
-
/**
|
|
6055
|
-
const
|
|
6056
|
-
/**
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
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
|
-
*
|
|
6101
|
-
*
|
|
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
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
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
|
-
/**
|
|
6174
|
-
const
|
|
6175
|
-
|
|
6176
|
-
|
|
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(
|
|
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$
|
|
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$
|
|
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
|
|
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
|
|
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
|
-
|
|
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("
|
|
8675
|
-
|
|
8676
|
-
|
|
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("
|
|
8681
|
-
|
|
10999
|
+
type: z.literal("notify_prefs"),
|
|
11000
|
+
prefs: NotifyPrefsSchema
|
|
8682
11001
|
}),
|
|
8683
11002
|
z.strictObject({
|
|
8684
|
-
type: z.literal("
|
|
8685
|
-
|
|
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("
|
|
8691
|
-
|
|
8692
|
-
|
|
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("
|
|
8696
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
|
-
|
|
8826
|
-
|
|
8827
|
-
|
|
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
|
-
|
|
8831
|
-
|
|
8832
|
-
|
|
8833
|
-
|
|
8834
|
-
|
|
8835
|
-
|
|
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
|
-
*
|
|
8839
|
-
*
|
|
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
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
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
|
-
*
|
|
8848
|
-
*
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
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
|
-
*
|
|
8858
|
-
*
|
|
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
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
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
|
-
*
|
|
8871
|
-
*
|
|
8872
|
-
*
|
|
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
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
8877
|
-
|
|
8878
|
-
|
|
8879
|
-
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
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
|
-
|
|
8886
|
-
/**
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
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
|
-
|
|
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 };
|