@rubytech/create-maxy-code 0.1.575 → 0.1.577

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.
Files changed (83) hide show
  1. package/dist/__tests__/preserve-house-cloudflare.test.js +2 -72
  2. package/dist/index.js +8 -59
  3. package/dist/preserve-house-cloudflare.js +2 -49
  4. package/package.json +1 -1
  5. package/payload/platform/lib/active-rules/dist/index.d.ts.map +1 -1
  6. package/payload/platform/lib/active-rules/dist/index.js +32 -1
  7. package/payload/platform/lib/active-rules/dist/index.js.map +1 -1
  8. package/payload/platform/lib/active-rules/src/index.test.ts +52 -3
  9. package/payload/platform/lib/active-rules/src/index.ts +36 -2
  10. package/payload/platform/lib/dispatch-read/dist/allocate.d.ts +57 -0
  11. package/payload/platform/lib/dispatch-read/dist/allocate.d.ts.map +1 -0
  12. package/payload/platform/lib/dispatch-read/dist/allocate.js +33 -0
  13. package/payload/platform/lib/dispatch-read/dist/allocate.js.map +1 -0
  14. package/payload/platform/lib/dispatch-read/dist/index.d.ts +2 -0
  15. package/payload/platform/lib/dispatch-read/dist/index.d.ts.map +1 -1
  16. package/payload/platform/lib/dispatch-read/dist/index.js +10 -1
  17. package/payload/platform/lib/dispatch-read/dist/index.js.map +1 -1
  18. package/payload/platform/lib/dispatch-read/src/__tests__/allocate.test.ts +41 -0
  19. package/payload/platform/lib/dispatch-read/src/allocate.ts +89 -0
  20. package/payload/platform/lib/dispatch-read/src/index.ts +14 -0
  21. package/payload/platform/plugins/admin/hooks/__tests__/prompt-optimiser-compliance.test.sh +7 -2
  22. package/payload/platform/plugins/admin/hooks/prompt-optimiser-compliance.sh +8 -2
  23. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +2 -2
  24. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +12 -0
  25. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-visit-status-write.test.ts +333 -22
  26. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-visits-scope.test.ts +262 -0
  27. package/payload/platform/plugins/cloudflare/skills/data-portal/schema.sql +126 -21
  28. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/visit-status.ts +142 -2
  29. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/visits.ts +118 -6
  30. package/payload/platform/plugins/dispatch/PLUGIN.md +2 -0
  31. package/payload/platform/plugins/dispatch/mcp/dist/lib/allocate.d.ts +19 -50
  32. package/payload/platform/plugins/dispatch/mcp/dist/lib/allocate.d.ts.map +1 -1
  33. package/payload/platform/plugins/dispatch/mcp/dist/lib/allocate.js +1 -28
  34. package/payload/platform/plugins/dispatch/mcp/dist/lib/allocate.js.map +1 -1
  35. package/payload/platform/plugins/dispatch/skills/dispatch-page/SKILL.md +45 -6
  36. package/payload/platform/plugins/dispatch/skills/dispatch-page/template/planner.css +60 -0
  37. package/payload/platform/plugins/dispatch/skills/dispatch-page/template/planner.html +13 -0
  38. package/payload/platform/plugins/dispatch/skills/dispatch-page/template/planner.js +203 -5
  39. package/payload/platform/plugins/docs/references/internals.md +1 -1
  40. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-path.test.d.ts +2 -0
  41. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-path.test.d.ts.map +1 -0
  42. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-path.test.js +19 -0
  43. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-path.test.js.map +1 -0
  44. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-register.test.d.ts +2 -0
  45. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-register.test.d.ts.map +1 -0
  46. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-register.test.js +110 -0
  47. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-register.test.js.map +1 -0
  48. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-verdict.test.d.ts +2 -0
  49. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-verdict.test.d.ts.map +1 -0
  50. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-verdict.test.js +22 -0
  51. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-verdict.test.js.map +1 -0
  52. package/payload/platform/plugins/telegram/mcp/dist/index.js +19 -163
  53. package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -1
  54. package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.d.ts +9 -0
  55. package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.d.ts.map +1 -1
  56. package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.js +11 -0
  57. package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.js.map +1 -1
  58. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.d.ts +30 -3
  59. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.d.ts.map +1 -1
  60. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.js +29 -4
  61. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.js.map +1 -1
  62. package/payload/platform/plugins/telegram/mcp/dist/tools/webhook-register.d.ts +33 -0
  63. package/payload/platform/plugins/telegram/mcp/dist/tools/webhook-register.d.ts.map +1 -0
  64. package/payload/platform/plugins/telegram/mcp/dist/tools/webhook-register.js +145 -0
  65. package/payload/platform/plugins/telegram/mcp/dist/tools/webhook-register.js.map +1 -0
  66. package/payload/platform/plugins/telegram/skills/configure/SKILL.md +4 -2
  67. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +5 -0
  68. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  69. package/payload/platform/services/claude-session-manager/dist/http-server.js +29 -0
  70. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  71. package/payload/platform/services/telegram-channel/dist/notification.d.ts +35 -3
  72. package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
  73. package/payload/platform/services/telegram-channel/dist/notification.js +38 -5
  74. package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
  75. package/payload/platform/services/telegram-channel/dist/server.js +1 -1
  76. package/payload/platform/services/telegram-channel/dist/server.js.map +1 -1
  77. package/payload/platform/services/webchat-channel/dist/notification.d.ts +2 -1
  78. package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
  79. package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
  80. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +2 -2
  81. package/payload/platform/templates/agents/admin/IDENTITY.md +1 -1
  82. package/payload/server/server.js +670 -155
  83. package/dist/__tests__/converge-client-admins.test.js +0 -50
@@ -6,10 +6,10 @@
6
6
  // Runs under `node --test dist/__tests__/*`.
7
7
  import test from "node:test";
8
8
  import assert from "node:assert/strict";
9
- import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync, rmSync, statSync } from "node:fs";
9
+ import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync, rmSync } from "node:fs";
10
10
  import { tmpdir } from "node:os";
11
11
  import { join } from "node:path";
12
- import { backupHouseCloudflare, restoreHouseCloudflare, migrateHouseCredentialFromAccount, } from "../preserve-house-cloudflare.js";
12
+ import { backupHouseCloudflare, restoreHouseCloudflare, } from "../preserve-house-cloudflare.js";
13
13
  function makeDirs() {
14
14
  const root = mkdtempSync(join(tmpdir(), "house-1664-"));
15
15
  const installDir = join(root, "install");
@@ -66,73 +66,3 @@ function acctSecrets(installDir, id, contents) {
66
66
  writeFileSync(p, contents);
67
67
  return p;
68
68
  }
69
- test("relocates a per-account master into the house file, mode 600, account untouched", () => {
70
- const { root, installDir } = makeDirs();
71
- try {
72
- const acct = acctSecrets(installDir, "acc-1", "CLOUDFLARE_API_TOKEN=cfat_minter\nCLOUDFLARE_ACCOUNT_ID=deadbeef\nCF_PAGES_D1_TOKEN=stale\n");
73
- const before = readFileSync(acct, "utf-8");
74
- const r = migrateHouseCredentialFromAccount(installDir);
75
- assert.equal(r.result, "relocated");
76
- assert.equal(r.source, "acc-1");
77
- const house = join(installDir, "platform/config", "cloudflare-house.env");
78
- assert.equal(readFileSync(house, "utf-8"), "CLOUDFLARE_API_TOKEN=cfat_minter\nCLOUDFLARE_ACCOUNT_ID=deadbeef\n", "only the two master keys are relocated, no stale scoped token");
79
- assert.equal(statSync(house).mode & 0o777, 0o600, "house file is mode 600");
80
- assert.equal(readFileSync(acct, "utf-8"), before, "account file untouched");
81
- }
82
- finally {
83
- rmSync(root, { recursive: true, force: true });
84
- }
85
- });
86
- test("no-op when the house file already exists (never overwrites)", () => {
87
- const { root, installDir, configDir } = makeDirs();
88
- try {
89
- const house = join(configDir, "cloudflare-house.env");
90
- writeFileSync(house, "CLOUDFLARE_API_TOKEN=existing\nCLOUDFLARE_ACCOUNT_ID=xyz\n");
91
- acctSecrets(installDir, "acc-1", "CLOUDFLARE_API_TOKEN=other\nCLOUDFLARE_ACCOUNT_ID=zzz\n");
92
- const r = migrateHouseCredentialFromAccount(installDir);
93
- assert.equal(r.result, "noop");
94
- assert.equal(readFileSync(house, "utf-8"), "CLOUDFLARE_API_TOKEN=existing\nCLOUDFLARE_ACCOUNT_ID=xyz\n", "existing house file is untouched");
95
- }
96
- finally {
97
- rmSync(root, { recursive: true, force: true });
98
- }
99
- });
100
- test("absent when no account carries a master (no house file, no error)", () => {
101
- const { root, installDir } = makeDirs();
102
- try {
103
- acctSecrets(installDir, "acc-1", "CF_PAGES_D1_TOKEN=scoped_only\n");
104
- const r = migrateHouseCredentialFromAccount(installDir);
105
- assert.equal(r.result, "absent");
106
- assert.ok(!existsSync(join(installDir, "platform/config", "cloudflare-house.env")));
107
- }
108
- finally {
109
- rmSync(root, { recursive: true, force: true });
110
- }
111
- });
112
- test("deterministic pick — lexicographically-first account id wins", () => {
113
- const { root, installDir } = makeDirs();
114
- try {
115
- acctSecrets(installDir, "bbb", "CLOUDFLARE_API_TOKEN=tok_b\nCLOUDFLARE_ACCOUNT_ID=idb\n");
116
- acctSecrets(installDir, "aaa", "CLOUDFLARE_API_TOKEN=tok_a\nCLOUDFLARE_ACCOUNT_ID=ida\n");
117
- const r = migrateHouseCredentialFromAccount(installDir);
118
- assert.equal(r.source, "aaa");
119
- assert.match(readFileSync(join(installDir, "platform/config", "cloudflare-house.env"), "utf-8"), /tok_a/);
120
- }
121
- finally {
122
- rmSync(root, { recursive: true, force: true });
123
- }
124
- });
125
- test("full upgrade path: pre-1631 relocates, second run is a no-op", () => {
126
- const { root, installDir, persistentDir } = makeDirs();
127
- try {
128
- // Pre-1631 upgrade: no house file survives the wipe/restore (nothing saved).
129
- simulateUpgrade(installDir, persistentDir);
130
- assert.ok(!existsSync(join(installDir, "platform/config", "cloudflare-house.env")));
131
- acctSecrets(installDir, "acc-1", "CLOUDFLARE_API_TOKEN=cfat_minter\nCLOUDFLARE_ACCOUNT_ID=deadbeef\n");
132
- assert.equal(migrateHouseCredentialFromAccount(installDir).result, "relocated");
133
- assert.equal(migrateHouseCredentialFromAccount(installDir).result, "noop", "second run is idempotent");
134
- }
135
- finally {
136
- rmSync(root, { recursive: true, force: true });
137
- }
138
- });
package/dist/index.js CHANGED
@@ -10,9 +10,8 @@ import { buildBrandCronBlock, mergeBrandCronBlock } from "./cron-registration.js
10
10
  import { validateTierFlag, validateEntitlementBase64, applyTierToAccountConfig, entitlementPath } from "./tier-flag.js";
11
11
  import { parseOsRelease, isUbuntuLike as isUbuntuLikePure, parseAptCacheCandidate, decideAptResolution, } from "./apt-resolve.js";
12
12
  import { findPeerBrandOnDefaultNeo4jPort } from "./peer-brand-detect.js";
13
- import { removeClientAdminsKey } from "./converge-client-admins.js";
14
13
  import { seedPublicInstructionFiles } from "./public-instruction-seed.js";
15
- import { backupHouseCloudflare, restoreHouseCloudflare, migrateHouseCredentialFromAccount, } from "./preserve-house-cloudflare.js";
14
+ import { backupHouseCloudflare, restoreHouseCloudflare, } from "./preserve-house-cloudflare.js";
16
15
  import { resolveNeo4jPidfileScrub } from "./neo4j-pidfile-scrub.js";
17
16
  import { runInitLogging } from "./init-logging.js";
18
17
  import { requireSupportedPlatform, detectPlatform } from "./platform-detect.js";
@@ -2650,15 +2649,6 @@ function deployPayload() {
2650
2649
  if (restoreHouseCloudflare(INSTALL_DIR, persistentDir)) {
2651
2650
  console.log(" Restored Cloudflare house credential (cloudflare-house.env preserved).");
2652
2651
  }
2653
- // Task 1671 — a pre-1631 install crossing the 0.1.449 boundary has no house
2654
- // credential yet, but its per-account secrets still carry the account-wide
2655
- // master (a minter, Task 1670). Relocate it into the house file so the broker
2656
- // and calendar find it where the 1631 readers expect it, with zero manual
2657
- // step. Idempotent: no-op once the house file exists (fresh install → absent).
2658
- {
2659
- const mig = migrateHouseCredentialFromAccount(INSTALL_DIR);
2660
- console.log(` [install] op=house-cred-migrate result=${mig.result}${mig.source ? ` source=${mig.source}` : ""}`);
2661
- }
2662
2652
  // users.json is read directly from persistentDir by both
2663
2653
  // platform/ui/app/lib/paths.ts (USERS_FILE = MAXY_DIR/users.json) and the
2664
2654
  // admin MCP plugin (USERS_FILE = CONFIG_DIR/users.json). No copy into the
@@ -2690,13 +2680,6 @@ function deployPayload() {
2690
2680
  // installer is its own npm package and doesn't bundle the platform lib.
2691
2681
  // Mirrors checkAdminAuthInvariant() in platform/lib/admins-write/src/index.ts;
2692
2682
  // future divergence between the two should be caught by the test suite.
2693
- // Task 1623 — converge existing client account.json to drop the vestigial
2694
- // admins[] key before the invariant check reads them. House untouched.
2695
- for (const r of removeClientAdminsKey(join(INSTALL_DIR, "data", "accounts"))) {
2696
- if (r.action === "removed" || r.action === "unreadable" || r.action === "write-failed") {
2697
- console.log(` [converge-1623] account=${r.accountId.slice(0, 8)} admins=${r.action}`);
2698
- }
2699
- }
2700
2683
  runInstallInvariantCheck(persistentUsersFile, join(INSTALL_DIR, "data", "accounts"));
2701
2684
  // The version marker is NOT written here. It is stamped at the very end of a
2702
2685
  // fully-successful install (after plugin registration and every throwing
@@ -3392,47 +3375,13 @@ function setupAccount() {
3392
3375
  logFile(` [neo4j] passing NEO4J_URI=${neo4jUri} to seed`);
3393
3376
  shell("bash", [seedScript], { cwd: INSTALL_DIR, env: neo4jEnv });
3394
3377
  }
3395
- // Task 2213 converge :Event.startDate onto canonical UTC. The dispatcher
3396
- // compares startDate to new Date().toISOString() LEXICALLY, so a row storing
3397
- // a +01:00 offset fires 60 minutes late under BST. Runs after the seed so the
3398
- // schema exists, with the same Neo4j env, and needs PLATFORM_ROOT to scope
3399
- // itself to this install's accounts on a shared Neo4j. Idempotent: a converged
3400
- // install rewrites nothing and prints op=done rewritten=0.
3401
- const convergeStartDate = join(INSTALL_DIR, "platform/plugins/scheduling/mcp/dist/scripts/converge-startdate-utc.js");
3402
- if (existsSync(convergeStartDate)) {
3403
- shell("node", [convergeStartDate], {
3404
- cwd: INSTALL_DIR,
3405
- env: { ...neo4jEnv, PLATFORM_ROOT: join(INSTALL_DIR, "platform") },
3406
- });
3407
- }
3408
- // Task 2361 — collapse the two routine description properties onto
3409
- // `:Event.description`, preferring whichever value the operator or the agent
3410
- // wrote. Runs after the seed for the same reasons the convergence above does:
3411
- // the schema exists by then, and the same Neo4j env is already built. Scoped to
3412
- // this install's accounts via PLATFORM_ROOT, so a co-resident brand sharing one
3413
- // Neo4j is untouched. Idempotent: a converged install matches nothing and
3414
- // prints converged=0.
3415
- const convergeRoutineDescription = join(INSTALL_DIR, "platform/plugins/scheduling/mcp/dist/scripts/converge-routine-description.js");
3416
- if (existsSync(convergeRoutineDescription)) {
3417
- shell("node", [convergeRoutineDescription], {
3418
- cwd: INSTALL_DIR,
3419
- env: { ...neo4jEnv, PLATFORM_ROOT: join(INSTALL_DIR, "platform") },
3420
- });
3421
- }
3422
- // Task 2513 — converge :Visit.startDate and :Visit.endDate onto canonical
3423
- // UTC. The planner window compares startDate to Z-suffixed bounds LEXICALLY,
3424
- // so a row storing a +01:00 offset lands in the wrong day under BST. Runs
3425
- // after the seed for the same reasons the two convergences above do, and is
3426
- // scoped to this install's accounts so a co-resident brand sharing one Neo4j
3427
- // is untouched. Idempotent: a converged install rewrites nothing and prints
3428
- // op=start-migration rewritten=0.
3429
- const convergeVisitInstants = join(INSTALL_DIR, "platform/plugins/dispatch/mcp/dist/scripts/converge-visit-instants.js");
3430
- if (existsSync(convergeVisitInstants)) {
3431
- shell("node", [convergeVisitInstants], {
3432
- cwd: INSTALL_DIR,
3433
- env: { ...neo4jEnv, PLATFORM_ROOT: join(INSTALL_DIR, "platform") },
3434
- });
3435
- }
3378
+ // Install-time data migrations were removed on 2026-08-08 by operator
3379
+ // decision. Three convergences ran here (Tasks 2213, 2361, 2513). Every
3380
+ // device in the fleet is past the state each one existed to repair, so on
3381
+ // every install they scanned and rewrote nothing while adding a Neo4j
3382
+ // round-trip and a way to fail: 2513's script leaked its driver and hung
3383
+ // the installer after printing scanned=0. A future migration belongs in a
3384
+ // one-shot the operator runs deliberately, not on the upgrade path.
3436
3385
  // Task 2460 — clear the brand-wide agent root this step used to write.
3437
3386
  // Every bundled specialist now reaches a session through the per-account
3438
3387
  // registry that `syncAccountAgentRegistry` mirrors at spawn from
@@ -6,8 +6,8 @@
6
6
  // calendar reconcile/publish scripts) fails with "house credential not found"
7
7
  // until an operator re-mints the token by hand. The persistent copy lives in
8
8
  // the brand's persistent config dir so even a full reinstall restores it.
9
- import { existsSync, cpSync, chmodSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
10
- import { join, dirname } from "node:path";
9
+ import { existsSync, cpSync, chmodSync } from "node:fs";
10
+ import { join } from "node:path";
11
11
  const HOUSE_CRED_FILE = "cloudflare-house.env";
12
12
  /**
13
13
  * Copy the live house credential (if any) into the persistent store before the
@@ -35,50 +35,3 @@ export function restoreHouseCloudflare(installDir, persistentDir) {
35
35
  chmodSync(dest, 0o600);
36
36
  return true;
37
37
  }
38
- /**
39
- * Task 1671 — on upgrade, if no house credential exists yet, relocate an
40
- * existing per-account master (CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID)
41
- * into the house file (mode 600). This lets a pre-1631 install cross the 0.1.449
42
- * boundary with zero manual step: the broker/calendar (Task 1670) then mint
43
- * their scoped tokens from the relocated minter where the 1631 readers expect it.
44
- *
45
- * Fail-safe and idempotent:
46
- * - never overwrites an existing house file (returns "noop");
47
- * - never guesses a token (both master keys required);
48
- * - deterministic: scans accounts sorted by id, takes the first with a master;
49
- * - relocates only the two master keys, so no per-account scoped token leaks
50
- * house-side.
51
- *
52
- * The relocated file is preserved on subsequent upgrades by backupHouseCloudflare
53
- * (Task 1664), which runs before the wipe on the next install.
54
- */
55
- export function migrateHouseCredentialFromAccount(installDir) {
56
- const house = join(installDir, "platform/config", HOUSE_CRED_FILE);
57
- if (existsSync(house))
58
- return { result: "noop" };
59
- const accountsDir = join(installDir, "data/accounts");
60
- if (!existsSync(accountsDir))
61
- return { result: "absent" };
62
- for (const id of readdirSync(accountsDir).sort()) {
63
- const secrets = join(accountsDir, id, "secrets", "cloudflare.env");
64
- if (!existsSync(secrets))
65
- continue;
66
- const env = {};
67
- for (const line of readFileSync(secrets, "utf8").split("\n")) {
68
- const m = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
69
- if (m)
70
- env[m[1]] = m[2].trim();
71
- }
72
- const apiToken = env.CLOUDFLARE_API_TOKEN;
73
- const accountId = env.CLOUDFLARE_ACCOUNT_ID;
74
- if (!apiToken || !accountId)
75
- continue;
76
- mkdirSync(dirname(house), { recursive: true });
77
- writeFileSync(house, `CLOUDFLARE_API_TOKEN=${apiToken}\nCLOUDFLARE_ACCOUNT_ID=${accountId}\n`, {
78
- mode: 0o600,
79
- });
80
- chmodSync(house, 0o600);
81
- return { result: "relocated", source: id };
82
- }
83
- return { result: "absent" };
84
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.575",
3
+ "version": "0.1.577",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,mBAAmB,OAAO,CAAC;AACxC,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAKD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAIpE;AAED,UAAU,cAAc;IACtB,GAAG,EAAE,CACH,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACvB,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CAC/D;AAMD,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,UAAU,GAAG,aAAa,CAAC;AACpE,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG,aAAa,CAAC;AAE/D,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAID,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,CAAC;CACzB;AAOD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxB;AAgED,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,EAAE,CAAC,CAE1B;AAKD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,oBAAoB,CAAC,CAI/B;AAID,wBAAsB,aAAa,CACjC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,UAAU,EAAE,CAAC,CAEvB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,mBAAmB,OAAO,CAAC;AACxC,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAgCD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAUpE;AAED,UAAU,cAAc;IACtB,GAAG,EAAE,CACH,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACvB,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CAC/D;AAMD,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,UAAU,GAAG,aAAa,CAAC;AACpE,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG,aAAa,CAAC;AAE/D,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAKD,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,CAAC;CACzB;AAOD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxB;AAgED,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,EAAE,CAAC,CAE1B;AAKD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,oBAAoB,CAAC,CAI/B;AAID,wBAAsB,aAAa,CACjC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,UAAU,EAAE,CAAC,CAEvB"}
@@ -20,11 +20,42 @@ exports.ACTIVE_CANDIDATE_FLOOR = 0.4;
20
20
  // Render every rule verbatim. Injection has no cap (Task 1951): concision is
21
21
  // enforced at write time by the conversational-memory skill so each stored
22
22
  // value is already short, not by truncating the block here.
23
+ //
24
+ // Task 2562 — the header names the owner and the block forbids reading a
25
+ // speaker out of it. Selection is owner-only (resolveOwnerUserId then
26
+ // queryOwnerCandidates below), so every rule here belongs to one identity the
27
+ // header never used to name, and a rule written in the third person about the
28
+ // owner read as a statement about whoever was typing: on sitedesk-code nine
29
+ // owner rules reached a second admin, who was then greeted by the owner's name.
30
+ //
31
+ // The block deliberately does NOT claim a sender is named elsewhere. The same
32
+ // block feeds all three doors and only WhatsApp carries an inbound provenance
33
+ // preamble today (whatsapp-channel/src/notification.ts composeInboundProvenance);
34
+ // webchat has none and Telegram has none until Task 2561 lands. Pointing the
35
+ // agent at a block that is absent is worse than saying nothing about it.
36
+ //
37
+ // Copy constraints, all load-bearing. No apostrophe anywhere in the block:
38
+ // plugins/admin/hooks/prompt-optimiser-compliance.sh holds its whole python
39
+ // program inside a single-quoted `python3 -c '...'` and its rules fixtures are
40
+ // single-quoted bash strings. No blank line: both strip regexes are non-greedy
41
+ // up to the blank line before `## Context`, so the body has to be one physical
42
+ // line. Operator-clean otherwise (no task numbers, repo paths or em-dashes) so
43
+ // the outbound redactor never trips on it.
44
+ //
45
+ // The header is an exact-match anchor in three places that must move with it:
46
+ // COMPOSED_ADMIN_FRAME (ui/app/lib/reader/parse-transcript.ts), the strip in
47
+ // plugins/admin/hooks/prompt-optimiser-compliance.sh, and that hook's test
48
+ // fixtures. Nothing logs when either strip stops matching, so the verbatim
49
+ // assertions in index.test.ts are the whole reconciliation.
23
50
  function formatStandingRulesBlock(rules) {
24
51
  if (rules.length === 0)
25
52
  return "";
26
53
  const lines = rules.map((r) => `- ${r.value}`);
27
- return `## Standing rules\n${lines.join("\n")}`;
54
+ return (`## Standing rules (account owner)\n` +
55
+ `These standing rules belong to the account owner. ` +
56
+ `Some of them may name the account owner in the third person. ` +
57
+ `None of them says who sent this turn, so do not infer from them who is speaking.\n` +
58
+ lines.join("\n"));
28
59
  }
29
60
  // The account's owner identity (Task 1461). One `AdminUser {role:'owner'}` is
30
61
  // seeded per account (Task 1359); `null` when absent (a pre-seeding legacy
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,uEAAuE;AACvE,sEAAsE;AACtE,gFAAgF;AAChF,8EAA8E;AAC9E,cAAc;;;AA0Dd,4DAUC;AAoCD,gDAgBC;AAgED,8DAKC;AAKD,gDAOC;AAID,sCAKC;AAhND,8EAA8E;AAC9E,gFAAgF;AAChF,4EAA4E;AAC5E,2EAA2E;AAC9D,QAAA,mBAAmB,GAAG,IAAI,CAAC;AAC3B,QAAA,sBAAsB,GAAG,GAAG,CAAC;AAqB1C,6EAA6E;AAC7E,2EAA2E;AAC3E,4DAA4D;AAC5D,EAAE;AACF,yEAAyE;AACzE,sEAAsE;AACtE,8EAA8E;AAC9E,8EAA8E;AAC9E,4EAA4E;AAC5E,gFAAgF;AAChF,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,kFAAkF;AAClF,6EAA6E;AAC7E,yEAAyE;AACzE,EAAE;AACF,2EAA2E;AAC3E,4EAA4E;AAC5E,+EAA+E;AAC/E,+EAA+E;AAC/E,+EAA+E;AAC/E,+EAA+E;AAC/E,2CAA2C;AAC3C,EAAE;AACF,8EAA8E;AAC9E,6EAA6E;AAC7E,2EAA2E;AAC3E,2EAA2E;AAC3E,4DAA4D;AAC5D,SAAgB,wBAAwB,CAAC,KAAmB;IAC1D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAC/C,OAAO,CACL,qCAAqC;QACrC,oDAAoD;QACpD,+DAA+D;QAC/D,oFAAoF;QACpF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CACjB,CAAC;AACJ,CAAC;AA+BD,8EAA8E;AAC9E,2EAA2E;AAC3E,6EAA6E;AAC7E,yEAAyE;AACzE,4EAA4E;AACrE,KAAK,UAAU,kBAAkB,CACtC,OAAuB,EACvB,SAAiB;IAEjB,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,CAC3B;;;;;KAKC,EACD,EAAE,SAAS,EAAE,CACd,CAAC;IACF,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9C,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AACzE,CAAC;AAED,mEAAmE;AACnE,8EAA8E;AAC9E,6EAA6E;AAC7E,8EAA8E;AAC9E,+EAA+E;AAC/E,gFAAgF;AAChF,KAAK,UAAU,oBAAoB,CACjC,OAAuB,EACvB,SAAiB,EACjB,WAAmB;IAEnB,yEAAyE;IACzE,8EAA8E;IAC9E,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAC9B;;;;;;;;;KASC,EACD,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,8BAAsB,EAAE,CAC1D,CAAC;IACF,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChC,YAAY,EAAE,CAAC,CAAC,GAAG,CAAC,cAAc,CAAW;QAC7C,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,UAAU,CAAW;QACrC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAW;QAC3B,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAW;QAC/B,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,YAAY,CAAW;QACzC,UAAU,EAAG,CAAC,CAAC,GAAG,CAAC,YAAY,CAAY,IAAI,EAAE;QACjD,SAAS,EAAG,CAAC,CAAC,GAAG,CAAC,WAAW,CAAqB,IAAI,IAAI;KAC3D,CAAC,CAAC,CAAC;AACN,CAAC;AAED,gFAAgF;AAChF,8EAA8E;AAC9E,6EAA6E;AAC7E,+DAA+D;AAC/D,KAAK,UAAU,iBAAiB,CAC9B,OAAuB,EACvB,SAAiB;IAMjB,IAAI,WAA0B,CAAC;IAC/B,IAAI,CAAC;QACH,WAAW,GAAG,MAAM,kBAAkB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IACtE,CAAC;IACD,IAAI,CAAC,WAAW;QAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACnF,MAAM,UAAU,GAAG,MAAM,oBAAoB,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;IAC/E,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AACtD,CAAC;AAED,2EAA2E;AAC3E,uEAAuE;AAChE,KAAK,UAAU,yBAAyB,CAC7C,OAAuB,EACvB,SAAiB;IAEjB,OAAO,CAAC,MAAM,iBAAiB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC;AAClE,CAAC;AAED,8EAA8E;AAC9E,8EAA8E;AAC9E,2EAA2E;AACpE,KAAK,UAAU,kBAAkB,CACtC,OAAuB,EACvB,SAAiB;IAEjB,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,iBAAiB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACxF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IAC3E,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;AACxC,CAAC;AAED,8EAA8E;AAC9E,+BAA+B;AACxB,KAAK,UAAU,aAAa,CACjC,OAAuB,EACvB,SAAiB;IAEjB,OAAO,CAAC,MAAM,kBAAkB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;AAC9D,CAAC"}
@@ -141,11 +141,60 @@ describe("resolveActiveRules — owner-scoped, uncapped, verbatim", () => {
141
141
  });
142
142
 
143
143
  describe("formatStandingRulesBlock", () => {
144
+ // Task 2562 — the header and the do-not-infer line are asserted verbatim
145
+ // because the same header string is an exact-match anchor in
146
+ // parse-transcript.ts (COMPOSED_ADMIN_FRAME) and in
147
+ // plugins/admin/hooks/prompt-optimiser-compliance.sh. Neither logs anything
148
+ // when it stops matching, so this assertion is the only thing standing
149
+ // between a copy edit and a silently broken strip.
150
+ const HEADER = "## Standing rules (account owner)";
151
+ const BODY =
152
+ "These standing rules belong to the account owner. Some of them may name the account owner in the third person. None of them says who sent this turn, so do not infer from them who is speaking.";
153
+
154
+ const oneRule = () => [
155
+ {
156
+ preferenceId: "p1",
157
+ category: "communication",
158
+ key: "k1",
159
+ value: "use British spelling",
160
+ confidence: 0.9,
161
+ observedAt: "2026-07-01T00:00:00Z",
162
+ },
163
+ ];
164
+
144
165
  it("returns empty string for no rules", () => {
145
166
  expect(formatStandingRulesBlock([])).toBe("");
146
167
  });
147
168
 
148
- it("renders every rule, with no cap", () => {
169
+ it("opens with the attributed header and the do-not-infer line", () => {
170
+ expect(formatStandingRulesBlock(oneRule())).toBe(
171
+ `${HEADER}\n${BODY}\n- use British spelling`,
172
+ );
173
+ });
174
+
175
+ it("carries no apostrophe, so the hook and its shell fixtures keep quoting", () => {
176
+ // The rule VALUES are operator data and may contain anything; only the two
177
+ // lines of platform copy are constrained.
178
+ const platformCopy = formatStandingRulesBlock(oneRule())
179
+ .split("\n")
180
+ .slice(0, 2)
181
+ .join("\n");
182
+ expect(platformCopy).not.toContain("'");
183
+ });
184
+
185
+ it("contains no blank line, so both strip regexes still terminate at ## Context", () => {
186
+ const rules = Array.from({ length: 3 }, (_, i) => ({
187
+ preferenceId: `p${i}`,
188
+ category: "communication",
189
+ key: `k${i}`,
190
+ value: `rule ${i}`,
191
+ confidence: 1 - i * 0.01,
192
+ observedAt: "2026-07-01T00:00:00Z",
193
+ }));
194
+ expect(formatStandingRulesBlock(rules)).not.toContain("\n\n");
195
+ });
196
+
197
+ it("renders every rule, with no cap, in resolution order", () => {
149
198
  const rules = Array.from({ length: 20 }, (_, i) => ({
150
199
  preferenceId: `p${i}`,
151
200
  category: "communication",
@@ -155,7 +204,7 @@ describe("formatStandingRulesBlock", () => {
155
204
  observedAt: "2026-07-01T00:00:00Z",
156
205
  }));
157
206
  const block = formatStandingRulesBlock(rules);
158
- expect(block).toContain("## Standing rules");
159
- expect((block.match(/^- /gm) ?? []).length).toBe(20);
207
+ expect(block.startsWith(`${HEADER}\n${BODY}\n`)).toBe(true);
208
+ expect(block.split("\n").slice(2)).toEqual(rules.map((r) => `- ${r.value}`));
160
209
  });
161
210
  });
@@ -33,10 +33,43 @@ export interface ActiveRule {
33
33
  // Render every rule verbatim. Injection has no cap (Task 1951): concision is
34
34
  // enforced at write time by the conversational-memory skill so each stored
35
35
  // value is already short, not by truncating the block here.
36
+ //
37
+ // Task 2562 — the header names the owner and the block forbids reading a
38
+ // speaker out of it. Selection is owner-only (resolveOwnerUserId then
39
+ // queryOwnerCandidates below), so every rule here belongs to one identity the
40
+ // header never used to name, and a rule written in the third person about the
41
+ // owner read as a statement about whoever was typing: on sitedesk-code nine
42
+ // owner rules reached a second admin, who was then greeted by the owner's name.
43
+ //
44
+ // The block deliberately does NOT claim a sender is named elsewhere. The same
45
+ // block feeds all three doors and only WhatsApp carries an inbound provenance
46
+ // preamble today (whatsapp-channel/src/notification.ts composeInboundProvenance);
47
+ // webchat has none and Telegram has none until Task 2561 lands. Pointing the
48
+ // agent at a block that is absent is worse than saying nothing about it.
49
+ //
50
+ // Copy constraints, all load-bearing. No apostrophe anywhere in the block:
51
+ // plugins/admin/hooks/prompt-optimiser-compliance.sh holds its whole python
52
+ // program inside a single-quoted `python3 -c '...'` and its rules fixtures are
53
+ // single-quoted bash strings. No blank line: both strip regexes are non-greedy
54
+ // up to the blank line before `## Context`, so the body has to be one physical
55
+ // line. Operator-clean otherwise (no task numbers, repo paths or em-dashes) so
56
+ // the outbound redactor never trips on it.
57
+ //
58
+ // The header is an exact-match anchor in three places that must move with it:
59
+ // COMPOSED_ADMIN_FRAME (ui/app/lib/reader/parse-transcript.ts), the strip in
60
+ // plugins/admin/hooks/prompt-optimiser-compliance.sh, and that hook's test
61
+ // fixtures. Nothing logs when either strip stops matching, so the verbatim
62
+ // assertions in index.test.ts are the whole reconciliation.
36
63
  export function formatStandingRulesBlock(rules: ActiveRule[]): string {
37
64
  if (rules.length === 0) return "";
38
65
  const lines = rules.map((r) => `- ${r.value}`);
39
- return `## Standing rules\n${lines.join("\n")}`;
66
+ return (
67
+ `## Standing rules (account owner)\n` +
68
+ `These standing rules belong to the account owner. ` +
69
+ `Some of them may name the account owner in the third person. ` +
70
+ `None of them says who sent this turn, so do not infer from them who is speaking.\n` +
71
+ lines.join("\n")
72
+ );
40
73
  }
41
74
 
42
75
  interface MinimalSession {
@@ -60,7 +93,8 @@ export interface ActiveRuleResolution {
60
93
  }
61
94
 
62
95
  // The per-turn injection payload the UI hands each channel gateway: the
63
- // formatted `## Standing rules` block plus the provenance the gateway logs.
96
+ // formatted `## Standing rules (account owner)` block plus the provenance the
97
+ // gateway logs.
64
98
  export interface StandingRulesInjection {
65
99
  block: string;
66
100
  ownerUserId: string | null;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Who can be sent to this job, and why the others cannot.
3
+ *
4
+ * The rule that matters: **a qualified worker who is already committed in the
5
+ * window is offered WITH the conflict shown, never dropped.** The office
6
+ * routinely double-books on purpose — a job runs short, a customer is on the
7
+ * way home, an engineer volunteers. A tool that silently removes the busy
8
+ * option makes that decision for them and gives no way to see it was made.
9
+ *
10
+ * Only a missing qualification excludes, and every exclusion is named with its
11
+ * worker and its reason, because `offered=0` with no explanation is the shape a
12
+ * quiet day and an unstaffable job type share.
13
+ *
14
+ * It lives in this shared lib rather than in the dispatch plugin because the
15
+ * portal visits push runs in `platform/ui` and cannot import a plugin's build
16
+ * output, and a second implementation of qualification and clash is the defect
17
+ * `functions/api/visits.ts:33-36` names: a gate written once per client is a
18
+ * gate got wrong once per client. Task 2510 moved `buildVisitKeyboard` here for
19
+ * the same reason. The dispatch plugin re-exports both from
20
+ * `mcp/src/lib/allocate.ts`, which is the import site its tools and tests
21
+ * already name.
22
+ */
23
+ import type { DispatchConfig } from "./types.js";
24
+ import type { Commitment } from "./read.js";
25
+ export type { Commitment };
26
+ export interface Candidate {
27
+ readonly workerId: string;
28
+ readonly name: string;
29
+ /** Commitments overlapping the requested window. Empty means free. */
30
+ readonly conflicts: readonly Commitment[];
31
+ }
32
+ export interface Exclusion {
33
+ readonly workerId: string;
34
+ readonly reason: "missing-qualification";
35
+ }
36
+ export interface SelectCandidatesParams {
37
+ readonly jobType: string;
38
+ readonly cfg: DispatchConfig;
39
+ /** Committed visits per workerId, already narrowed to the window. */
40
+ readonly commitments: ReadonlyMap<string, readonly Commitment[]>;
41
+ }
42
+ export interface CandidateSelection {
43
+ readonly offered: readonly Candidate[];
44
+ readonly excluded: readonly Exclusion[];
45
+ /** The qualification the job type demands, echoed for the log line. */
46
+ readonly required: string;
47
+ }
48
+ export declare function selectCandidates(params: SelectCandidatesParams): CandidateSelection;
49
+ /** `[a, b)` overlap. Touching endpoints do not overlap. */
50
+ export declare function overlaps(a: {
51
+ start: string;
52
+ end: string;
53
+ }, b: {
54
+ start: string;
55
+ end: string;
56
+ }): boolean;
57
+ //# sourceMappingURL=allocate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"allocate.d.ts","sourceRoot":"","sources":["../src/allocate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,YAAY,EAAE,UAAU,EAAE,CAAC;AAE3B,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,SAAS,EAAE,SAAS,UAAU,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;CAC1C;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC,CAAC;CAClE;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAC;IACxC,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,kBAAkB,CAuBnF;AAED,2DAA2D;AAC3D,wBAAgB,QAAQ,CACtB,CAAC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EACjC,CAAC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAChC,OAAO,CAET"}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.selectCandidates = selectCandidates;
4
+ exports.overlaps = overlaps;
5
+ function selectCandidates(params) {
6
+ const { jobType, cfg, commitments } = params;
7
+ const type = cfg.jobTypes.find((t) => t.name === jobType);
8
+ if (!type) {
9
+ // An unknown job type is the caller's error, not a staffing outcome. Every
10
+ // worker is excluded for a reason that would be a lie, so none are: the
11
+ // caller checks the type before asking.
12
+ return { offered: [], excluded: [], required: "" };
13
+ }
14
+ const offered = [];
15
+ const excluded = [];
16
+ for (const worker of cfg.workers) {
17
+ if (!worker.qualifications.includes(type.requiredQualification)) {
18
+ excluded.push({ workerId: worker.workerId, reason: "missing-qualification" });
19
+ continue;
20
+ }
21
+ offered.push({
22
+ workerId: worker.workerId,
23
+ name: worker.name,
24
+ conflicts: commitments.get(worker.workerId) ?? [],
25
+ });
26
+ }
27
+ return { offered, excluded, required: type.requiredQualification };
28
+ }
29
+ /** `[a, b)` overlap. Touching endpoints do not overlap. */
30
+ function overlaps(a, b) {
31
+ return a.start < b.end && b.start < a.end;
32
+ }
33
+ //# sourceMappingURL=allocate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"allocate.js","sourceRoot":"","sources":["../src/allocate.ts"],"names":[],"mappings":";;AAyDA,4CAuBC;AAGD,4BAKC;AA/BD,SAAgB,gBAAgB,CAAC,MAA8B;IAC7D,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAC7C,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;IAC1D,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,2EAA2E;QAC3E,wEAAwE;QACxE,wCAAwC;QACxC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IACrD,CAAC;IACD,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,MAAM,QAAQ,GAAgB,EAAE,CAAC;IACjC,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,qBAAqB,CAAC,EAAE,CAAC;YAChE,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC,CAAC;YAC9E,SAAS;QACX,CAAC;QACD,OAAO,CAAC,IAAI,CAAC;YACX,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;SAClD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,qBAAqB,EAAE,CAAC;AACrE,CAAC;AAED,2DAA2D;AAC3D,SAAgB,QAAQ,CACtB,CAAiC,EACjC,CAAiC;IAEjC,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC;AAC5C,CAAC"}
@@ -14,6 +14,8 @@ export { RECORDABLE_FIELDS, loadDispatchConfig, terminalJobStatuses, terminalVis
14
14
  export type { RecordableField } from "./config.js";
15
15
  export { CALLBACK_DATA_MAX_BYTES, buildVisitKeyboard, dispatchCallbackData, parseDispatchCallback, } from "./keyboard.js";
16
16
  export type { DispatchButton, VisitKeyboard } from "./keyboard.js";
17
+ export { overlaps, selectCandidates } from "./allocate.js";
18
+ export type { Candidate, CandidateSelection, Exclusion, SelectCandidatesParams, } from "./allocate.js";
17
19
  export { withSession } from "./session.js";
18
20
  export { resolveAccountZone } from "./zone.js";
19
21
  export { MAX_RANGE_DAYS, addDaysYmd, currentWeekYmd, daysBetween, minutesIntoDay, ymdInZone, zonedDayStartUtc, zonedRangeBounds, } from "./window.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAKnD,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EACL,cAAc,EACd,UAAU,EACV,cAAc,EACd,WAAW,EACX,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,eAAe,EACf,SAAS,EACT,UAAU,EACV,cAAc,EACd,OAAO,EACP,QAAQ,EACR,UAAU,EACV,uBAAuB,EACvB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AAInB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAGhD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE3F,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACvF,mBAAmB,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAKnD,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAQnE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC3D,YAAY,EACV,SAAS,EACT,kBAAkB,EAClB,SAAS,EACT,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EACL,cAAc,EACd,UAAU,EACV,cAAc,EACd,WAAW,EACX,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,eAAe,EACf,SAAS,EACT,UAAU,EACV,cAAc,EACd,OAAO,EACP,QAAQ,EACR,UAAU,EACV,uBAAuB,EACvB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AAInB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAGhD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE3F,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACvF,mBAAmB,YAAY,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.readWorkerDay = exports.readJobList = exports.readDay = exports.readCounts = exports.nonZuluStarts = exports.geofenceVerdict = exports.distanceM = exports.readVisitsInWindow = exports.readVisitsForJob = exports.readVisitRecordState = exports.readVisit = exports.readSiteLocation = exports.readSiteHistory = exports.readPreviousVisits = exports.readOpenVisitsForWorker = exports.readNodeId = exports.readJobs = exports.readJob = exports.readFieldVisit = exports.readDrafts = exports.readDraft = exports.readCommitments = exports.projectVisit = exports.projectJob = exports.projectFieldVisit = exports.buildJobListQuery = exports.zonedRangeBounds = exports.zonedDayStartUtc = exports.ymdInZone = exports.minutesIntoDay = exports.daysBetween = exports.currentWeekYmd = exports.addDaysYmd = exports.MAX_RANGE_DAYS = exports.resolveAccountZone = exports.withSession = exports.parseDispatchCallback = exports.dispatchCallbackData = exports.buildVisitKeyboard = exports.CALLBACK_DATA_MAX_BYTES = exports.workerForTelegramUser = exports.terminalVisitStatuses = exports.terminalJobStatuses = exports.loadDispatchConfig = exports.RECORDABLE_FIELDS = void 0;
3
+ exports.readWorkerDay = exports.readJobList = exports.readDay = exports.readCounts = exports.nonZuluStarts = exports.geofenceVerdict = exports.distanceM = exports.readVisitsInWindow = exports.readVisitsForJob = exports.readVisitRecordState = exports.readVisit = exports.readSiteLocation = exports.readSiteHistory = exports.readPreviousVisits = exports.readOpenVisitsForWorker = exports.readNodeId = exports.readJobs = exports.readJob = exports.readFieldVisit = exports.readDrafts = exports.readDraft = exports.readCommitments = exports.projectVisit = exports.projectJob = exports.projectFieldVisit = exports.buildJobListQuery = exports.zonedRangeBounds = exports.zonedDayStartUtc = exports.ymdInZone = exports.minutesIntoDay = exports.daysBetween = exports.currentWeekYmd = exports.addDaysYmd = exports.MAX_RANGE_DAYS = exports.resolveAccountZone = exports.withSession = exports.selectCandidates = exports.overlaps = exports.parseDispatchCallback = exports.dispatchCallbackData = exports.buildVisitKeyboard = exports.CALLBACK_DATA_MAX_BYTES = exports.workerForTelegramUser = exports.terminalVisitStatuses = exports.terminalJobStatuses = exports.loadDispatchConfig = exports.RECORDABLE_FIELDS = void 0;
4
4
  /**
5
5
  * Dispatch reads, shared by the MCP tools and the office planner.
6
6
  *
@@ -27,6 +27,15 @@ Object.defineProperty(exports, "CALLBACK_DATA_MAX_BYTES", { enumerable: true, ge
27
27
  Object.defineProperty(exports, "buildVisitKeyboard", { enumerable: true, get: function () { return keyboard_js_1.buildVisitKeyboard; } });
28
28
  Object.defineProperty(exports, "dispatchCallbackData", { enumerable: true, get: function () { return keyboard_js_1.dispatchCallbackData; } });
29
29
  Object.defineProperty(exports, "parseDispatchCallback", { enumerable: true, get: function () { return keyboard_js_1.parseDispatchCallback; } });
30
+ // Task 2511 — qualification and clash, shared by the dispatch tools and the
31
+ // portal visits push. One implementation, for the reason allocate.ts states:
32
+ // the push runs in platform/ui and cannot import a plugin's build output, and a
33
+ // second copy of these rules is a gate got wrong once per client.
34
+ // `Commitment` is NOT re-exported here: it is `readCommitments`'s own shape,
35
+ // declared once in read.ts and already exported below.
36
+ var allocate_js_1 = require("./allocate.js");
37
+ Object.defineProperty(exports, "overlaps", { enumerable: true, get: function () { return allocate_js_1.overlaps; } });
38
+ Object.defineProperty(exports, "selectCandidates", { enumerable: true, get: function () { return allocate_js_1.selectCandidates; } });
30
39
  var session_js_1 = require("./session.js");
31
40
  Object.defineProperty(exports, "withSession", { enumerable: true, get: function () { return session_js_1.withSession; } });
32
41
  var zone_js_1 = require("./zone.js");
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;GAWG;AACH,yCAMqB;AALnB,8GAAA,iBAAiB,OAAA;AACjB,+GAAA,kBAAkB,OAAA;AAClB,gHAAA,mBAAmB,OAAA;AACnB,kHAAA,qBAAqB,OAAA;AACrB,kHAAA,qBAAqB,OAAA;AAIvB,6EAA6E;AAC7E,6EAA6E;AAC7E,oEAAoE;AACpE,6CAKuB;AAJrB,sHAAA,uBAAuB,OAAA;AACvB,iHAAA,kBAAkB,OAAA;AAClB,mHAAA,oBAAoB,OAAA;AACpB,oHAAA,qBAAqB,OAAA;AAIvB,2CAA2C;AAAlC,yGAAA,WAAW,OAAA;AACpB,qCAA+C;AAAtC,6GAAA,kBAAkB,OAAA;AAC3B,yCASqB;AARnB,2GAAA,cAAc,OAAA;AACd,uGAAA,UAAU,OAAA;AACV,2GAAA,cAAc,OAAA;AACd,wGAAA,WAAW,OAAA;AACX,2GAAA,cAAc,OAAA;AACd,sGAAA,SAAS,OAAA;AACT,6GAAA,gBAAgB,OAAA;AAChB,6GAAA,gBAAgB,OAAA;AAGlB,qEAAqE;AACrE,qCAoBmB;AAnBjB,4GAAA,iBAAiB,OAAA;AACjB,4GAAA,iBAAiB,OAAA;AACjB,qGAAA,UAAU,OAAA;AACV,uGAAA,YAAY,OAAA;AACZ,0GAAA,eAAe,OAAA;AACf,oGAAA,SAAS,OAAA;AACT,qGAAA,UAAU,OAAA;AACV,yGAAA,cAAc,OAAA;AACd,kGAAA,OAAO,OAAA;AACP,mGAAA,QAAQ,OAAA;AACR,qGAAA,UAAU,OAAA;AACV,kHAAA,uBAAuB,OAAA;AACvB,6GAAA,kBAAkB,OAAA;AAClB,0GAAA,eAAe,OAAA;AACf,2GAAA,gBAAgB,OAAA;AAChB,oGAAA,SAAS,OAAA;AACT,+GAAA,oBAAoB,OAAA;AACpB,2GAAA,gBAAgB,OAAA;AAChB,6GAAA,kBAAkB,OAAA;AAGpB,uEAAuE;AACvE,kDAAkD;AAClD,mCAAsD;AAA7C,mGAAA,SAAS,OAAA;AAAE,yGAAA,eAAe,OAAA;AAGnC,yEAAyE;AACzE,qCAA2F;AAAlF,wGAAA,aAAa,OAAA;AAAE,qGAAA,UAAU,OAAA;AAAE,kGAAA,OAAO,OAAA;AAAE,sGAAA,WAAW,OAAA;AAAE,wGAAA,aAAa,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;GAWG;AACH,yCAMqB;AALnB,8GAAA,iBAAiB,OAAA;AACjB,+GAAA,kBAAkB,OAAA;AAClB,gHAAA,mBAAmB,OAAA;AACnB,kHAAA,qBAAqB,OAAA;AACrB,kHAAA,qBAAqB,OAAA;AAIvB,6EAA6E;AAC7E,6EAA6E;AAC7E,oEAAoE;AACpE,6CAKuB;AAJrB,sHAAA,uBAAuB,OAAA;AACvB,iHAAA,kBAAkB,OAAA;AAClB,mHAAA,oBAAoB,OAAA;AACpB,oHAAA,qBAAqB,OAAA;AAIvB,4EAA4E;AAC5E,6EAA6E;AAC7E,gFAAgF;AAChF,kEAAkE;AAClE,6EAA6E;AAC7E,uDAAuD;AACvD,6CAA2D;AAAlD,uGAAA,QAAQ,OAAA;AAAE,+GAAA,gBAAgB,OAAA;AAQnC,2CAA2C;AAAlC,yGAAA,WAAW,OAAA;AACpB,qCAA+C;AAAtC,6GAAA,kBAAkB,OAAA;AAC3B,yCASqB;AARnB,2GAAA,cAAc,OAAA;AACd,uGAAA,UAAU,OAAA;AACV,2GAAA,cAAc,OAAA;AACd,wGAAA,WAAW,OAAA;AACX,2GAAA,cAAc,OAAA;AACd,sGAAA,SAAS,OAAA;AACT,6GAAA,gBAAgB,OAAA;AAChB,6GAAA,gBAAgB,OAAA;AAGlB,qEAAqE;AACrE,qCAoBmB;AAnBjB,4GAAA,iBAAiB,OAAA;AACjB,4GAAA,iBAAiB,OAAA;AACjB,qGAAA,UAAU,OAAA;AACV,uGAAA,YAAY,OAAA;AACZ,0GAAA,eAAe,OAAA;AACf,oGAAA,SAAS,OAAA;AACT,qGAAA,UAAU,OAAA;AACV,yGAAA,cAAc,OAAA;AACd,kGAAA,OAAO,OAAA;AACP,mGAAA,QAAQ,OAAA;AACR,qGAAA,UAAU,OAAA;AACV,kHAAA,uBAAuB,OAAA;AACvB,6GAAA,kBAAkB,OAAA;AAClB,0GAAA,eAAe,OAAA;AACf,2GAAA,gBAAgB,OAAA;AAChB,oGAAA,SAAS,OAAA;AACT,+GAAA,oBAAoB,OAAA;AACpB,2GAAA,gBAAgB,OAAA;AAChB,6GAAA,kBAAkB,OAAA;AAGpB,uEAAuE;AACvE,kDAAkD;AAClD,mCAAsD;AAA7C,mGAAA,SAAS,OAAA;AAAE,yGAAA,eAAe,OAAA;AAGnC,yEAAyE;AACzE,qCAA2F;AAAlF,wGAAA,aAAa,OAAA;AAAE,qGAAA,UAAU,OAAA;AAAE,kGAAA,OAAO,OAAA;AAAE,sGAAA,WAAW,OAAA;AAAE,wGAAA,aAAa,OAAA"}