@rubytech/create-maxy-code 0.1.566 → 0.1.568

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 (176) hide show
  1. package/dist/__tests__/brand-agent-root.test.js +91 -0
  2. package/dist/brand-agent-root.js +58 -0
  3. package/dist/index.js +25 -28
  4. package/package.json +1 -1
  5. package/payload/platform/neo4j/schema.cypher +9 -0
  6. package/payload/platform/package-lock.json +66 -1
  7. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  8. package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +1 -0
  9. package/payload/platform/plugins/admin/mcp/dist/index.js +7 -1
  10. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  11. package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.d.ts +10 -1
  12. package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.d.ts.map +1 -1
  13. package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.js +6 -1
  14. package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.js.map +1 -1
  15. package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts +9 -0
  16. package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts.map +1 -1
  17. package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js +1 -1
  18. package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js.map +1 -1
  19. package/payload/platform/plugins/admin/skills/capabilities-here/SKILL.md +1 -1
  20. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +91 -1
  21. package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +5 -1
  22. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +13 -0
  23. package/payload/platform/plugins/docs/references/channel-wake-and-prompt.md +90 -0
  24. package/payload/platform/plugins/inventory/.claude-plugin/plugin.json +21 -0
  25. package/payload/platform/plugins/inventory/PLUGIN.md +75 -0
  26. package/payload/platform/plugins/inventory/lib/mcp-spawn-tee/index.js +204 -0
  27. package/payload/platform/plugins/inventory/lib/mcp-spawn-tee/package.json +3 -0
  28. package/payload/platform/plugins/inventory/mcp/dist/index.d.ts +2 -0
  29. package/payload/platform/plugins/inventory/mcp/dist/index.d.ts.map +1 -0
  30. package/payload/platform/plugins/inventory/mcp/dist/index.js +241 -0
  31. package/payload/platform/plugins/inventory/mcp/dist/index.js.map +1 -0
  32. package/payload/platform/plugins/inventory/mcp/dist/lib/forecast.d.ts +20 -0
  33. package/payload/platform/plugins/inventory/mcp/dist/lib/forecast.d.ts.map +1 -0
  34. package/payload/platform/plugins/inventory/mcp/dist/lib/forecast.js +29 -0
  35. package/payload/platform/plugins/inventory/mcp/dist/lib/forecast.js.map +1 -0
  36. package/payload/platform/plugins/inventory/mcp/dist/lib/neo4j.d.ts +5 -0
  37. package/payload/platform/plugins/inventory/mcp/dist/lib/neo4j.d.ts.map +1 -0
  38. package/payload/platform/plugins/inventory/mcp/dist/lib/neo4j.js +40 -0
  39. package/payload/platform/plugins/inventory/mcp/dist/lib/neo4j.js.map +1 -0
  40. package/payload/platform/plugins/inventory/mcp/dist/lib/read.d.ts +28 -0
  41. package/payload/platform/plugins/inventory/mcp/dist/lib/read.d.ts.map +1 -0
  42. package/payload/platform/plugins/inventory/mcp/dist/lib/read.js +91 -0
  43. package/payload/platform/plugins/inventory/mcp/dist/lib/read.js.map +1 -0
  44. package/payload/platform/plugins/inventory/mcp/dist/lib/replenish.d.ts +56 -0
  45. package/payload/platform/plugins/inventory/mcp/dist/lib/replenish.d.ts.map +1 -0
  46. package/payload/platform/plugins/inventory/mcp/dist/lib/replenish.js +107 -0
  47. package/payload/platform/plugins/inventory/mcp/dist/lib/replenish.js.map +1 -0
  48. package/payload/platform/plugins/inventory/mcp/dist/lib/types.d.ts +98 -0
  49. package/payload/platform/plugins/inventory/mcp/dist/lib/types.d.ts.map +1 -0
  50. package/payload/platform/plugins/inventory/mcp/dist/lib/types.js +7 -0
  51. package/payload/platform/plugins/inventory/mcp/dist/lib/types.js.map +1 -0
  52. package/payload/platform/plugins/inventory/mcp/dist/lib/write-rejects.d.ts +12 -0
  53. package/payload/platform/plugins/inventory/mcp/dist/lib/write-rejects.d.ts.map +1 -0
  54. package/payload/platform/plugins/inventory/mcp/dist/lib/write-rejects.js +49 -0
  55. package/payload/platform/plugins/inventory/mcp/dist/lib/write-rejects.js.map +1 -0
  56. package/payload/platform/plugins/inventory/mcp/dist/tools/demand-record.d.ts +20 -0
  57. package/payload/platform/plugins/inventory/mcp/dist/tools/demand-record.d.ts.map +1 -0
  58. package/payload/platform/plugins/inventory/mcp/dist/tools/demand-record.js +53 -0
  59. package/payload/platform/plugins/inventory/mcp/dist/tools/demand-record.js.map +1 -0
  60. package/payload/platform/plugins/inventory/mcp/dist/tools/forecast.d.ts +23 -0
  61. package/payload/platform/plugins/inventory/mcp/dist/tools/forecast.d.ts.map +1 -0
  62. package/payload/platform/plugins/inventory/mcp/dist/tools/forecast.js +18 -0
  63. package/payload/platform/plugins/inventory/mcp/dist/tools/forecast.js.map +1 -0
  64. package/payload/platform/plugins/inventory/mcp/dist/tools/policy-set.d.ts +25 -0
  65. package/payload/platform/plugins/inventory/mcp/dist/tools/policy-set.d.ts.map +1 -0
  66. package/payload/platform/plugins/inventory/mcp/dist/tools/policy-set.js +37 -0
  67. package/payload/platform/plugins/inventory/mcp/dist/tools/policy-set.js.map +1 -0
  68. package/payload/platform/plugins/inventory/mcp/dist/tools/purchase-order-record.d.ts +25 -0
  69. package/payload/platform/plugins/inventory/mcp/dist/tools/purchase-order-record.d.ts.map +1 -0
  70. package/payload/platform/plugins/inventory/mcp/dist/tools/purchase-order-record.js +43 -0
  71. package/payload/platform/plugins/inventory/mcp/dist/tools/purchase-order-record.js.map +1 -0
  72. package/payload/platform/plugins/inventory/mcp/dist/tools/replenish-plan.d.ts +29 -0
  73. package/payload/platform/plugins/inventory/mcp/dist/tools/replenish-plan.d.ts.map +1 -0
  74. package/payload/platform/plugins/inventory/mcp/dist/tools/replenish-plan.js +95 -0
  75. package/payload/platform/plugins/inventory/mcp/dist/tools/replenish-plan.js.map +1 -0
  76. package/payload/platform/plugins/inventory/mcp/dist/tools/stock-record.d.ts +21 -0
  77. package/payload/platform/plugins/inventory/mcp/dist/tools/stock-record.d.ts.map +1 -0
  78. package/payload/platform/plugins/inventory/mcp/dist/tools/stock-record.js +46 -0
  79. package/payload/platform/plugins/inventory/mcp/dist/tools/stock-record.js.map +1 -0
  80. package/payload/platform/plugins/inventory/mcp/dist/tools/supplier-terms-set.d.ts +24 -0
  81. package/payload/platform/plugins/inventory/mcp/dist/tools/supplier-terms-set.d.ts.map +1 -0
  82. package/payload/platform/plugins/inventory/mcp/dist/tools/supplier-terms-set.js +29 -0
  83. package/payload/platform/plugins/inventory/mcp/dist/tools/supplier-terms-set.js.map +1 -0
  84. package/payload/platform/plugins/inventory/mcp/package.json +21 -0
  85. package/payload/platform/plugins/memory/references/schema-retail.md +20 -0
  86. package/payload/platform/scripts/lib/provision-account-dir.sh +0 -2
  87. package/payload/platform/services/claude-session-manager/dist/account-agent-registry.d.ts +19 -0
  88. package/payload/platform/services/claude-session-manager/dist/account-agent-registry.d.ts.map +1 -0
  89. package/payload/platform/services/claude-session-manager/dist/account-agent-registry.js +146 -0
  90. package/payload/platform/services/claude-session-manager/dist/account-agent-registry.js.map +1 -0
  91. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  92. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +8 -0
  93. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  94. package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts +5 -0
  95. package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -1
  96. package/payload/platform/services/claude-session-manager/dist/fs-watcher.js +5 -5
  97. package/payload/platform/services/claude-session-manager/dist/fs-watcher.js.map +1 -1
  98. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +11 -1
  99. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  100. package/payload/platform/services/claude-session-manager/dist/http-server.js +124 -17
  101. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  102. package/payload/platform/services/claude-session-manager/dist/index.js +25 -13
  103. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  104. package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts +6 -1
  105. package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts.map +1 -1
  106. package/payload/platform/services/claude-session-manager/dist/pty-census.js +6 -3
  107. package/payload/platform/services/claude-session-manager/dist/pty-census.js.map +1 -1
  108. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +21 -8
  109. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  110. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +38 -15
  111. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  112. package/payload/platform/services/claude-session-manager/dist/public-spawn-model.d.ts +28 -0
  113. package/payload/platform/services/claude-session-manager/dist/public-spawn-model.d.ts.map +1 -0
  114. package/payload/platform/services/claude-session-manager/dist/public-spawn-model.js +58 -0
  115. package/payload/platform/services/claude-session-manager/dist/public-spawn-model.js.map +1 -0
  116. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +2 -0
  117. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  118. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +4 -1
  119. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  120. package/payload/platform/services/claude-session-manager/dist/reaper.d.ts +3 -0
  121. package/payload/platform/services/claude-session-manager/dist/reaper.d.ts.map +1 -1
  122. package/payload/platform/services/claude-session-manager/dist/reaper.js +2 -1
  123. package/payload/platform/services/claude-session-manager/dist/reaper.js.map +1 -1
  124. package/payload/platform/services/claude-session-manager/dist/session-cap-audit.d.ts +2 -0
  125. package/payload/platform/services/claude-session-manager/dist/session-cap-audit.d.ts.map +1 -1
  126. package/payload/platform/services/claude-session-manager/dist/session-cap-audit.js +5 -2
  127. package/payload/platform/services/claude-session-manager/dist/session-cap-audit.js.map +1 -1
  128. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts +2 -0
  129. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts.map +1 -1
  130. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js +5 -3
  131. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js.map +1 -1
  132. package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts +26 -16
  133. package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts.map +1 -1
  134. package/payload/platform/services/claude-session-manager/dist/systemd-scope.js +84 -31
  135. package/payload/platform/services/claude-session-manager/dist/systemd-scope.js.map +1 -1
  136. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts +7 -0
  137. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts.map +1 -1
  138. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js +6 -0
  139. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js.map +1 -1
  140. package/payload/platform/services/telegram-channel/dist/notification.d.ts +10 -2
  141. package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
  142. package/payload/platform/services/telegram-channel/dist/notification.js +15 -3
  143. package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
  144. package/payload/platform/services/telegram-channel/dist/server.d.ts +5 -0
  145. package/payload/platform/services/telegram-channel/dist/server.d.ts.map +1 -1
  146. package/payload/platform/services/telegram-channel/dist/server.js +57 -5
  147. package/payload/platform/services/telegram-channel/dist/server.js.map +1 -1
  148. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts +44 -2
  149. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts.map +1 -1
  150. package/payload/platform/services/telegram-channel/dist/turn-follow.js +188 -15
  151. package/payload/platform/services/telegram-channel/dist/turn-follow.js.map +1 -1
  152. package/payload/platform/services/webchat-channel/dist/server.d.ts +5 -0
  153. package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
  154. package/payload/platform/services/webchat-channel/dist/server.js +53 -4
  155. package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
  156. package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts +44 -2
  157. package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts.map +1 -1
  158. package/payload/platform/services/webchat-channel/dist/turn-follow.js +188 -15
  159. package/payload/platform/services/webchat-channel/dist/turn-follow.js.map +1 -1
  160. package/payload/platform/services/whatsapp-channel/dist/instructions.d.ts +9 -0
  161. package/payload/platform/services/whatsapp-channel/dist/instructions.d.ts.map +1 -0
  162. package/payload/platform/services/whatsapp-channel/dist/instructions.js +22 -0
  163. package/payload/platform/services/whatsapp-channel/dist/instructions.js.map +1 -0
  164. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +3 -1
  165. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  166. package/payload/platform/services/whatsapp-channel/dist/notification.js +10 -2
  167. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  168. package/payload/platform/services/whatsapp-channel/dist/server.d.ts +10 -2
  169. package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
  170. package/payload/platform/services/whatsapp-channel/dist/server.js +71 -8
  171. package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
  172. package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts +34 -2
  173. package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts.map +1 -1
  174. package/payload/platform/services/whatsapp-channel/dist/turn-follow.js +153 -15
  175. package/payload/platform/services/whatsapp-channel/dist/turn-follow.js.map +1 -1
  176. package/payload/server/server.js +254 -96
@@ -0,0 +1,91 @@
1
+ const num = (v) => typeof v?.toNumber === "function"
2
+ ? v.toNumber()
3
+ : Number(v);
4
+ const numOrNull = (v) => v === null || v === undefined ? null : num(v);
5
+ /** Everything the plan needs for one location, in one query. OPTIONAL MATCH
6
+ * throughout rather than MATCH: a product missing its policy or supplier
7
+ * terms must still come back, as a skipped line naming what is missing. A
8
+ * plain MATCH would drop it and the plan would look complete while silently
9
+ * omitting exactly the products that need operator attention. */
10
+ export async function readPlanCandidates(session, accountId, location) {
11
+ const res = await session.run(`MATCH (sp:StockPosition {accountId: $accountId, location: $location})
12
+ MATCH (p:Product {accountId: $accountId}) WHERE elementId(p) = sp.product
13
+ OPTIONAL MATCH (pol:ReplenishmentPolicy {accountId: $accountId, product: sp.product, location: $location})
14
+ OPTIONAL MATCH (p)-[terms:SUPPLIED_BY]->(o:Organization {accountId: $accountId})
15
+ OPTIONAL MATCH (d:DemandObservation {accountId: $accountId, product: sp.product, location: $location})
16
+ WITH sp, p, pol, terms, o, d ORDER BY d.periodStart ASC
17
+ WITH sp, p, pol, terms, o, collect(d.quantitySold) AS history
18
+ RETURN p.sku AS sku,
19
+ sp.product AS productNodeId,
20
+ sp.location AS location,
21
+ sp.quantityOnHand AS quantityOnHand,
22
+ sp.quantityOnOrder AS quantityOnOrder,
23
+ sp.asOf AS asOf,
24
+ history,
25
+ pol.serviceLevel AS serviceLevel,
26
+ pol.reviewPeriodDays AS reviewPeriodDays,
27
+ terms.leadTimeDays AS leadTimeDays,
28
+ terms.minimumOrderQuantity AS minimumOrderQuantity,
29
+ terms.orderMultiple AS orderMultiple,
30
+ CASE WHEN o IS NULL THEN null ELSE elementId(o) END AS supplierNodeId`, { accountId, location });
31
+ return res.records.map((r) => ({
32
+ sku: r.get("sku"),
33
+ productNodeId: r.get("productNodeId"),
34
+ location: r.get("location"),
35
+ quantityOnHand: num(r.get("quantityOnHand")),
36
+ quantityOnOrder: num(r.get("quantityOnOrder")),
37
+ asOf: r.get("asOf"),
38
+ history: r.get("history").map(num),
39
+ serviceLevel: numOrNull(r.get("serviceLevel")),
40
+ reviewPeriodDays: numOrNull(r.get("reviewPeriodDays")),
41
+ leadTimeDays: numOrNull(r.get("leadTimeDays")),
42
+ minimumOrderQuantity: numOrNull(r.get("minimumOrderQuantity")),
43
+ orderMultiple: numOrNull(r.get("orderMultiple")),
44
+ supplierNodeId: r.get("supplierNodeId") ?? null,
45
+ }));
46
+ }
47
+ /** Resolves a SKU to a :Product element id on this account. Null when absent
48
+ * or when the SKU is ambiguous — two products sharing a SKU is a data fault
49
+ * the operator must fix, not something to guess past. */
50
+ export async function resolveProductBySku(session, accountId, sku) {
51
+ const res = await session.run(`MATCH (p:Product {accountId: $accountId, sku: $sku})
52
+ RETURN elementId(p) AS id
53
+ LIMIT 2`, { accountId, sku });
54
+ if (res.records.length !== 1)
55
+ return null;
56
+ return res.records[0].get("id");
57
+ }
58
+ /** Units sold per period for one product at one location, oldest first. */
59
+ export async function readDemandHistory(session, accountId, productNodeId, location) {
60
+ const res = await session.run(`MATCH (d:DemandObservation {accountId: $accountId, product: $productNodeId, location: $location})
61
+ RETURN d.quantitySold AS q
62
+ ORDER BY d.periodStart ASC`, { accountId, productNodeId, location });
63
+ return res.records.map((r) => {
64
+ const q = r.get("q");
65
+ return typeof q?.toNumber === "function" ? q.toNumber() : Number(q);
66
+ });
67
+ }
68
+ /** Sets the commercial terms on an existing SUPPLIED_BY edge. Returns false
69
+ * when no such edge exists: the edge is the operator's assertion that this
70
+ * supplier supplies this product, and creating it here would let a typo
71
+ * invent a supply relationship. */
72
+ export async function setSupplierTerms(session, accountId, productNodeId, supplierNodeId, terms) {
73
+ const res = await session.run(`MATCH (p:Product {accountId: $accountId})-[r:SUPPLIED_BY]->(o:Organization {accountId: $accountId})
74
+ WHERE elementId(p) = $productNodeId AND elementId(o) = $supplierNodeId
75
+ SET r.leadTimeDays = $leadTimeDays,
76
+ r.minimumOrderQuantity = $minimumOrderQuantity,
77
+ r.orderMultiple = $orderMultiple,
78
+ r.unitCost = $unitCost
79
+ RETURN count(r) AS n`, { accountId, productNodeId, supplierNodeId, ...terms });
80
+ const n = res.records[0]?.get("n");
81
+ return (typeof n?.toNumber === "function" ? n.toNumber() : Number(n ?? 0)) > 0;
82
+ }
83
+ /** Confirms a :StoreLocation element id exists on this account. */
84
+ export async function resolveLocation(session, accountId, nodeId) {
85
+ const res = await session.run(`MATCH (l:StoreLocation {accountId: $accountId})
86
+ WHERE elementId(l) = $nodeId
87
+ RETURN count(l) AS n`, { accountId, nodeId });
88
+ const n = res.records[0]?.get("n");
89
+ return (typeof n?.toNumber === "function" ? n.toNumber() : Number(n ?? 0)) === 1;
90
+ }
91
+ //# sourceMappingURL=read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/lib/read.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Task 2448 — the replenishment arithmetic.
3
+ *
4
+ * Pure functions over plain numbers, deliberately free of any graph or driver
5
+ * import, so the whole of the decision maths is testable without a database.
6
+ */
7
+ /** Population standard deviation. Returns 0 for zero or one value, where
8
+ * spread is undefined, rather than NaN: NaN would propagate silently into a
9
+ * reorder point and surface as a nonsense order quantity. */
10
+ export declare function standardDeviation(values: number[]): number;
11
+ export declare function serviceLevelZ(serviceLevel: number): number;
12
+ export interface ReorderPointInput {
13
+ /** Expected units sold per period, from the forecast. */
14
+ perPeriod: number;
15
+ /** Spread of units sold per period, across the same history. */
16
+ stdDevPerPeriod: number;
17
+ /** Length of one demand period in days. */
18
+ periodDays: number;
19
+ leadTimeDays: number;
20
+ reviewPeriodDays: number;
21
+ serviceLevel: number;
22
+ }
23
+ /**
24
+ * The stock level at which to reorder.
25
+ *
26
+ * Expected demand across the whole exposure window (the lead time plus the
27
+ * review period, because an order placed just after a review waits a full
28
+ * cycle before the next chance), plus a buffer scaled by how much demand
29
+ * actually varies over that same window.
30
+ */
31
+ export declare function reorderPoint(input: ReorderPointInput): number;
32
+ export interface OrderQuantityInput {
33
+ /** Order-up-to level, which is the reorder point under periodic review. */
34
+ target: number;
35
+ /** On hand plus already on order. */
36
+ available: number;
37
+ minimumOrderQuantity: number;
38
+ orderMultiple: number;
39
+ }
40
+ /**
41
+ * How much to order.
42
+ *
43
+ * Order sequence matters and is the usual source of bugs: raise to the
44
+ * supplier's minimum FIRST, then round up to their pack multiple. Doing it the
45
+ * other way round can round a raised quantity back below the minimum when the
46
+ * minimum is not itself a multiple of the pack size.
47
+ */
48
+ export declare function orderQuantity(input: OrderQuantityInput): number;
49
+ /**
50
+ * How many days the stock on hand lasts at the expected rate.
51
+ *
52
+ * Null when nothing is expected to sell: cover is undefined then, and
53
+ * returning a large number would rank a dead product alongside a healthy one.
54
+ */
55
+ export declare function daysOfCover(quantityOnHand: number, perPeriod: number, periodDays: number): number | null;
56
+ //# sourceMappingURL=replenish.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replenish.d.ts","sourceRoot":"","sources":["../../src/lib/replenish.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;8DAE8D;AAC9D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAK1D;AAsBD,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAmB1D;AAED,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,eAAe,EAAE,MAAM,CAAC;IACxB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAwB7D;AAED,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAc/D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CAQf"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Task 2448 — the replenishment arithmetic.
3
+ *
4
+ * Pure functions over plain numbers, deliberately free of any graph or driver
5
+ * import, so the whole of the decision maths is testable without a database.
6
+ */
7
+ /** Population standard deviation. Returns 0 for zero or one value, where
8
+ * spread is undefined, rather than NaN: NaN would propagate silently into a
9
+ * reorder point and surface as a nonsense order quantity. */
10
+ export function standardDeviation(values) {
11
+ if (values.length < 2)
12
+ return 0;
13
+ const mean = values.reduce((sum, n) => sum + n, 0) / values.length;
14
+ const variance = values.reduce((sum, n) => sum + (n - mean) ** 2, 0) / values.length;
15
+ return Math.sqrt(variance);
16
+ }
17
+ /**
18
+ * How many standard deviations of buffer a given service level needs.
19
+ *
20
+ * A lookup table with linear interpolation rather than an inverse normal
21
+ * function: the table is exact at the levels operators actually pick, it is
22
+ * readable by anyone auditing a recommendation, and it needs no special
23
+ * function implementation. Levels between table points interpolate; the
24
+ * function refuses outside the open interval rather than clamping, because a
25
+ * clamp would silently answer a question the operator did not ask.
26
+ */
27
+ const Z_TABLE = [
28
+ [0.5, 0],
29
+ [0.8, 0.8416],
30
+ [0.9, 1.2816],
31
+ [0.95, 1.6449],
32
+ [0.975, 1.96],
33
+ [0.99, 2.3263],
34
+ [0.999, 3.0902],
35
+ ];
36
+ export function serviceLevelZ(serviceLevel) {
37
+ if (!(serviceLevel > 0 && serviceLevel < 1)) {
38
+ throw new Error(`[inventory] serviceLevelZ: service level must be strictly between 0 and 1, got ${serviceLevel}`);
39
+ }
40
+ if (serviceLevel <= Z_TABLE[0][0])
41
+ return Z_TABLE[0][1];
42
+ const last = Z_TABLE[Z_TABLE.length - 1];
43
+ if (serviceLevel >= last[0])
44
+ return last[1];
45
+ for (let i = 1; i < Z_TABLE.length; i += 1) {
46
+ const [hiLevel, hiZ] = Z_TABLE[i];
47
+ if (serviceLevel <= hiLevel) {
48
+ const [loLevel, loZ] = Z_TABLE[i - 1];
49
+ const t = (serviceLevel - loLevel) / (hiLevel - loLevel);
50
+ return loZ + t * (hiZ - loZ);
51
+ }
52
+ }
53
+ return last[1];
54
+ }
55
+ /**
56
+ * The stock level at which to reorder.
57
+ *
58
+ * Expected demand across the whole exposure window (the lead time plus the
59
+ * review period, because an order placed just after a review waits a full
60
+ * cycle before the next chance), plus a buffer scaled by how much demand
61
+ * actually varies over that same window.
62
+ */
63
+ export function reorderPoint(input) {
64
+ const { perPeriod, stdDevPerPeriod, periodDays, leadTimeDays, reviewPeriodDays, serviceLevel, } = input;
65
+ if (!(periodDays > 0)) {
66
+ throw new Error(`[inventory] reorderPoint: periodDays must be positive, got ${periodDays}`);
67
+ }
68
+ const exposureDays = leadTimeDays + reviewPeriodDays;
69
+ const exposurePeriods = exposureDays / periodDays;
70
+ const expected = perPeriod * exposurePeriods;
71
+ const buffer = serviceLevelZ(serviceLevel) * stdDevPerPeriod * Math.sqrt(exposurePeriods);
72
+ return expected + buffer;
73
+ }
74
+ /**
75
+ * How much to order.
76
+ *
77
+ * Order sequence matters and is the usual source of bugs: raise to the
78
+ * supplier's minimum FIRST, then round up to their pack multiple. Doing it the
79
+ * other way round can round a raised quantity back below the minimum when the
80
+ * minimum is not itself a multiple of the pack size.
81
+ */
82
+ export function orderQuantity(input) {
83
+ const { target, available, minimumOrderQuantity, orderMultiple } = input;
84
+ if (!(orderMultiple > 0)) {
85
+ throw new Error(`[inventory] orderQuantity: order multiple must be positive, got ${orderMultiple}`);
86
+ }
87
+ const gap = target - available;
88
+ if (gap <= 0)
89
+ return 0;
90
+ const raised = Math.max(gap, minimumOrderQuantity);
91
+ return Math.ceil(raised / orderMultiple) * orderMultiple;
92
+ }
93
+ /**
94
+ * How many days the stock on hand lasts at the expected rate.
95
+ *
96
+ * Null when nothing is expected to sell: cover is undefined then, and
97
+ * returning a large number would rank a dead product alongside a healthy one.
98
+ */
99
+ export function daysOfCover(quantityOnHand, perPeriod, periodDays) {
100
+ if (!(periodDays > 0)) {
101
+ throw new Error(`[inventory] daysOfCover: periodDays must be positive, got ${periodDays}`);
102
+ }
103
+ if (perPeriod <= 0)
104
+ return null;
105
+ return quantityOnHand / (perPeriod / periodDays);
106
+ }
107
+ //# sourceMappingURL=replenish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replenish.js","sourceRoot":"","sources":["../../src/lib/replenish.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Task 2448 — shared shapes for the inventory tools.
3
+ *
4
+ * `write` and the `resolve*` reads are injected rather than imported by the
5
+ * tool modules so unit tests exercise the decision logic without loading the
6
+ * Neo4j driver. `index.ts` passes the real implementations.
7
+ */
8
+ import type { Session } from "neo4j-driver";
9
+ /**
10
+ * The real graph-write contracts, imported rather than redeclared. A
11
+ * redeclared GraphRelationship carrying `targetLabel` instead of
12
+ * `targetNodeId` shipped a dead write path in the ledger plugin: every create
13
+ * was rejected at runtime with `reason=unresolved-target requested=1 found=0`.
14
+ * Importing the real types makes that a compile error. Never redeclare them.
15
+ */
16
+ import type { CreatedBy, GraphRelationship } from "../../../../../lib/graph-write/dist/index.js";
17
+ export type { CreatedBy, GraphRelationship };
18
+ export interface WriteParams {
19
+ session: Session;
20
+ labels: string[];
21
+ props: Record<string, unknown>;
22
+ relationships: GraphRelationship[];
23
+ createdBy: CreatedBy;
24
+ }
25
+ export interface WriteResult {
26
+ nodesCreated?: number;
27
+ edgesCreated?: number;
28
+ }
29
+ export type WriteFn = (params: WriteParams) => Promise<WriteResult>;
30
+ export interface ToolResult {
31
+ content: Array<{
32
+ type: "text";
33
+ text: string;
34
+ }>;
35
+ isError?: boolean;
36
+ }
37
+ export declare function ok(text: string): ToolResult;
38
+ export declare function fail(text: string): ToolResult;
39
+ /** One stock line as the agent parsed it out of an upload. */
40
+ export interface StockRow {
41
+ /** Product SKU. Resolved to a :Product element id by the tool. */
42
+ sku: string;
43
+ /** Element id of an existing :StoreLocation. */
44
+ location: string;
45
+ quantityOnHand: number;
46
+ quantityOnOrder: number;
47
+ /** ISO timestamp the count was taken. */
48
+ asOf: string;
49
+ }
50
+ /** One period of observed demand for one product at one location. */
51
+ export interface DemandRow {
52
+ sku: string;
53
+ location: string;
54
+ periodStart: string;
55
+ periodEnd: string;
56
+ quantitySold: number;
57
+ }
58
+ /** Everything the plan needs about one product at one location, read in a
59
+ * single query. Any of the optional fields being absent is what produces a
60
+ * skipped line rather than a guessed one. */
61
+ export interface PlanCandidate {
62
+ sku: string;
63
+ productNodeId: string;
64
+ location: string;
65
+ quantityOnHand: number;
66
+ quantityOnOrder: number;
67
+ /** ISO timestamp of the stock count. */
68
+ asOf: string;
69
+ /** Units sold per period, oldest first. Empty when no history exists. */
70
+ history: number[];
71
+ serviceLevel: number | null;
72
+ reviewPeriodDays: number | null;
73
+ leadTimeDays: number | null;
74
+ minimumOrderQuantity: number | null;
75
+ orderMultiple: number | null;
76
+ supplierNodeId: string | null;
77
+ }
78
+ export interface PlanLine {
79
+ sku: string;
80
+ location: string;
81
+ daysOfCover: number | null;
82
+ reorderPoint: number;
83
+ recommendedQuantity: number;
84
+ supplierNodeId: string;
85
+ /** ISO date the order would arrive if placed today. */
86
+ expectedArrival: string;
87
+ }
88
+ export type PlanMissing = "leadTime" | "serviceLevel" | "history";
89
+ export interface PlanSkip {
90
+ sku: string;
91
+ location: string;
92
+ missing: PlanMissing;
93
+ }
94
+ /** Resolves a SKU to a :Product element id on this account, or null. */
95
+ export type ResolveProductFn = (session: Session, accountId: string, sku: string) => Promise<string | null>;
96
+ /** Confirms a :StoreLocation element id exists on this account. */
97
+ export type ResolveLocationFn = (session: Session, accountId: string, nodeId: string) => Promise<boolean>;
98
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export function ok(text) {
2
+ return { content: [{ type: "text", text }] };
3
+ }
4
+ export function fail(text) {
5
+ return { content: [{ type: "text", text }], isError: true };
6
+ }
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AA0CA,MAAM,UAAU,EAAE,CAAC,IAAY;IAC7B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,IAAY;IAC/B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Counts `[graph-write] reject` lines across the inventory MCP tee logs
3
+ * modified within the trailing 24 hours.
4
+ *
5
+ * Uses file mtime rather than parsing per-line timestamps: the tee log carries
6
+ * no timestamp of its own, and a file untouched for a day cannot hold a reject
7
+ * from the last one. Best-effort; an unreadable log directory yields 0 rather
8
+ * than throwing, because a census that dies on a missing directory tells the
9
+ * operator nothing at all.
10
+ */
11
+ export declare function countWriteRejects24h(logDir: string, now: number): number;
12
+ //# sourceMappingURL=write-rejects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-rejects.d.ts","sourceRoot":"","sources":["../../src/lib/write-rejects.ts"],"names":[],"mappings":"AAeA;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAuBxE"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Task 2448 — count recent write rejections from the inventory MCP logs.
3
+ *
4
+ * A rejected write cannot record its own failure in the graph, because the
5
+ * whole point is that nothing was written. The only durable trace is the
6
+ * stderr line graph-write emits, which mcp-spawn-tee captures per session.
7
+ * Counting those lines is what lets an operator distinguish a healthy empty
8
+ * inventory from one whose write path is dead. The ledger plugin printed
9
+ * those two states identically for a full day on 2026-07-18.
10
+ */
11
+ import { readdirSync, readFileSync, statSync } from "node:fs";
12
+ import { join } from "node:path";
13
+ const DAY_MS = 86_400_000;
14
+ /**
15
+ * Counts `[graph-write] reject` lines across the inventory MCP tee logs
16
+ * modified within the trailing 24 hours.
17
+ *
18
+ * Uses file mtime rather than parsing per-line timestamps: the tee log carries
19
+ * no timestamp of its own, and a file untouched for a day cannot hold a reject
20
+ * from the last one. Best-effort; an unreadable log directory yields 0 rather
21
+ * than throwing, because a census that dies on a missing directory tells the
22
+ * operator nothing at all.
23
+ */
24
+ export function countWriteRejects24h(logDir, now) {
25
+ let entries;
26
+ try {
27
+ entries = readdirSync(logDir).filter((f) => f.startsWith("mcp-inventory-") && f.endsWith(".log"));
28
+ }
29
+ catch {
30
+ return 0;
31
+ }
32
+ let total = 0;
33
+ for (const name of entries) {
34
+ const path = join(logDir, name);
35
+ try {
36
+ if (now - statSync(path).mtimeMs > DAY_MS)
37
+ continue;
38
+ for (const line of readFileSync(path, "utf-8").split("\n")) {
39
+ if (line.includes("[graph-write] reject"))
40
+ total += 1;
41
+ }
42
+ }
43
+ catch {
44
+ continue;
45
+ }
46
+ }
47
+ return total;
48
+ }
49
+ //# sourceMappingURL=write-rejects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-rejects.js","sourceRoot":"","sources":["../../src/lib/write-rejects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,MAAM,GAAG,UAAU,CAAC;AAE1B;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,GAAW;IAC9D,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,CAClC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAC5D,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC;YACH,IAAI,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,GAAG,MAAM;gBAAE,SAAS;YACpD,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC3D,IAAI,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC;oBAAE,KAAK,IAAI,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Task 2448 — append observed demand for one product at one location.
3
+ *
4
+ * A zero-sales period is accepted deliberately: it is the difference between
5
+ * "sold nothing" and "no data", and collapsing the two would make the forecast
6
+ * read a quiet week as an absent one.
7
+ */
8
+ import type { Session } from "neo4j-driver";
9
+ import { type CreatedBy, type DemandRow, type ResolveLocationFn, type ResolveProductFn, type ToolResult, type WriteFn } from "../lib/types.js";
10
+ export interface DemandRecordParams {
11
+ session: Session;
12
+ write: WriteFn;
13
+ resolveProduct: ResolveProductFn;
14
+ resolveLocation: ResolveLocationFn;
15
+ accountId: string;
16
+ createdBy: CreatedBy;
17
+ rows: DemandRow[];
18
+ }
19
+ export declare function demandRecord(params: DemandRecordParams): Promise<ToolResult>;
20
+ //# sourceMappingURL=demand-record.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demand-record.d.ts","sourceRoot":"","sources":["../../src/tools/demand-record.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EACL,KAAK,SAAS,EACd,KAAK,SAAS,EAEd,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,OAAO,EAGb,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,gBAAgB,CAAC;IACjC,eAAe,EAAE,iBAAiB,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAoElF"}
@@ -0,0 +1,53 @@
1
+ import { fail, ok, } from "../lib/types.js";
2
+ export async function demandRecord(params) {
3
+ const { session, write, resolveProduct, resolveLocation, accountId, createdBy, rows } = params;
4
+ if (rows.length === 0) {
5
+ return fail(`[inventory] op=reject reason=empty-batch accountId=${accountId} — no demand rows supplied. Nothing was written.`);
6
+ }
7
+ let accepted = 0;
8
+ const rejected = [];
9
+ for (const row of rows) {
10
+ const sku = row.sku.trim();
11
+ const location = row.location.trim();
12
+ if (!(Date.parse(row.periodEnd) > Date.parse(row.periodStart))) {
13
+ rejected.push(`${sku} (period ${row.periodStart}..${row.periodEnd} does not advance)`);
14
+ continue;
15
+ }
16
+ if (!(row.quantitySold >= 0)) {
17
+ rejected.push(`${sku} (negative quantity ${row.quantitySold})`);
18
+ continue;
19
+ }
20
+ const productNodeId = await resolveProduct(session, accountId, sku);
21
+ if (productNodeId === null) {
22
+ rejected.push(`${sku} (unknown sku)`);
23
+ continue;
24
+ }
25
+ if (!(await resolveLocation(session, accountId, location))) {
26
+ rejected.push(`${sku} (unknown location ${location})`);
27
+ continue;
28
+ }
29
+ const relationships = [
30
+ { type: "OF_PRODUCT", direction: "outgoing", targetNodeId: productNodeId },
31
+ { type: "AT_LOCATION", direction: "outgoing", targetNodeId: location },
32
+ ];
33
+ await write({
34
+ session,
35
+ labels: ["DemandObservation"],
36
+ props: {
37
+ accountId,
38
+ product: productNodeId,
39
+ location,
40
+ periodStart: row.periodStart,
41
+ periodEnd: row.periodEnd,
42
+ quantitySold: row.quantitySold,
43
+ },
44
+ relationships,
45
+ createdBy,
46
+ });
47
+ accepted += 1;
48
+ }
49
+ console.error(`[inventory] op=demand-record accountId=${accountId} rows=${rows.length} accepted=${accepted} rejected=${rejected.length}`);
50
+ const tail = rejected.length === 0 ? "" : ` Rejected: ${rejected.join("; ")}.`;
51
+ return ok(`op=demand-record rows=${rows.length} accepted=${accepted} rejected=${rejected.length}.${tail}`);
52
+ }
53
+ //# sourceMappingURL=demand-record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demand-record.js","sourceRoot":"","sources":["../../src/tools/demand-record.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Task 2448 — expected demand per period for one product at one location.
3
+ *
4
+ * Nothing is stored. The number is derived from :DemandObservation on every
5
+ * call, which is why replacing the method in Task 2449 needs no migration.
6
+ * An empty history is an error, not a forecast of zero: those two mean
7
+ * opposite things to an operator deciding whether to order.
8
+ */
9
+ import type { Session } from "neo4j-driver";
10
+ import { type ResolveProductFn, type ToolResult } from "../lib/types.js";
11
+ export type ReadHistoryFn = (session: Session, accountId: string, productNodeId: string, location: string) => Promise<number[]>;
12
+ export interface ForecastParams {
13
+ session: Session;
14
+ resolveProduct: ResolveProductFn;
15
+ readHistory: ReadHistoryFn;
16
+ accountId: string;
17
+ sku: string;
18
+ location: string;
19
+ window: number;
20
+ }
21
+ export declare const FORECAST_METHOD = "moving-average";
22
+ export declare function forecast(params: ForecastParams): Promise<ToolResult>;
23
+ //# sourceMappingURL=forecast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forecast.d.ts","sourceRoot":"","sources":["../../src/tools/forecast.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAY,MAAM,iBAAiB,CAAC;AAEnF,MAAM,MAAM,aAAa,GAAG,CAC1B,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AAEvB,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,gBAAgB,CAAC;IACjC,WAAW,EAAE,aAAa,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAEhD,wBAAsB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CA2B1E"}
@@ -0,0 +1,18 @@
1
+ import { movingAverage } from "../lib/forecast.js";
2
+ import { fail, ok } from "../lib/types.js";
3
+ export const FORECAST_METHOD = "moving-average";
4
+ export async function forecast(params) {
5
+ const { session, resolveProduct, readHistory, accountId, sku, location, window } = params;
6
+ const productNodeId = await resolveProduct(session, accountId, sku.trim());
7
+ if (productNodeId === null) {
8
+ return fail(`[inventory] op=reject reason=unknown-sku sku=${sku.trim()} — no product with that SKU on this account, or more than one.`);
9
+ }
10
+ const history = await readHistory(session, accountId, productNodeId, location.trim());
11
+ if (history.length === 0) {
12
+ return fail(`[inventory] op=reject reason=no-history sku=${sku.trim()} location=${location.trim()} — record demand with inventory-demand-record first. No history is not the same as no demand, so no figure is returned.`);
13
+ }
14
+ const perPeriod = movingAverage(history, window);
15
+ console.error(`[inventory] op=forecast accountId=${accountId} method=${FORECAST_METHOD} window=${window} periods=${history.length} sku=${sku.trim()} location=${location.trim()} perPeriod=${perPeriod}`);
16
+ return ok(`op=forecast method=${FORECAST_METHOD} window=${window} periods=${history.length} perPeriod=${perPeriod}. Expected demand for ${sku.trim()} is ${perPeriod} units per period, averaged over the trailing ${Math.min(window, history.length)} of ${history.length} recorded periods.`);
17
+ }
18
+ //# sourceMappingURL=forecast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forecast.js","sourceRoot":"","sources":["../../src/tools/forecast.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAA0C,IAAI,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAmBnF,MAAM,CAAC,MAAM,eAAe,GAAG,gBAAgB,CAAC;AAEhD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,MAAsB;IACnD,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,GAC9E,MAAM,CAAC;IAET,MAAM,aAAa,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3E,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;QAC3B,OAAO,IAAI,CACT,gDAAgD,GAAG,CAAC,IAAI,EAAE,gEAAgE,CAC3H,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACtF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CACT,+CAA+C,GAAG,CAAC,IAAI,EAAE,aAAa,QAAQ,CAAC,IAAI,EAAE,yHAAyH,CAC/M,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAEjD,OAAO,CAAC,KAAK,CACX,qCAAqC,SAAS,WAAW,eAAe,WAAW,MAAM,YAAY,OAAO,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,aAAa,QAAQ,CAAC,IAAI,EAAE,cAAc,SAAS,EAAE,CAC3L,CAAC;IAEF,OAAO,EAAE,CACP,sBAAsB,eAAe,WAAW,MAAM,YAAY,OAAO,CAAC,MAAM,cAAc,SAAS,yBAAyB,GAAG,CAAC,IAAI,EAAE,OAAO,SAAS,iDAAiD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,OAAO,CAAC,MAAM,oBAAoB,CACrR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Task 2448 — set the replenishment policy for one product at one location.
3
+ *
4
+ * A service level of exactly 1 is refused rather than clamped: perfect
5
+ * availability needs infinite safety stock, and silently substituting 0.99
6
+ * would hand the operator a number they did not choose.
7
+ */
8
+ import type { Session } from "neo4j-driver";
9
+ import { type CreatedBy, type ResolveLocationFn, type ResolveProductFn, type ToolResult, type WriteFn } from "../lib/types.js";
10
+ export interface PolicySetParams {
11
+ session: Session;
12
+ write: WriteFn;
13
+ resolveProduct: ResolveProductFn;
14
+ resolveLocation: ResolveLocationFn;
15
+ accountId: string;
16
+ createdBy: CreatedBy;
17
+ sku: string;
18
+ location: string;
19
+ /** Target in-stock probability, strictly between 0 and 1. */
20
+ serviceLevel: number;
21
+ /** How often the operator actually reviews and reorders, in days. */
22
+ reviewPeriodDays: number;
23
+ }
24
+ export declare function policySet(params: PolicySetParams): Promise<ToolResult>;
25
+ //# sourceMappingURL=policy-set.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-set.d.ts","sourceRoot":"","sources":["../../src/tools/policy-set.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EACL,KAAK,SAAS,EAEd,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,OAAO,EAGb,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,gBAAgB,CAAC;IACjC,eAAe,EAAE,iBAAiB,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,YAAY,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CA+D5E"}