@rubytech/create-realagent-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.
- package/dist/__tests__/brand-agent-root.test.js +91 -0
- package/dist/brand-agent-root.js +58 -0
- package/dist/index.js +25 -28
- package/package.json +1 -1
- package/payload/platform/neo4j/schema.cypher +9 -0
- package/payload/platform/package-lock.json +66 -1
- package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
- package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +1 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js +7 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.d.ts +10 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.js +6 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts +9 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js.map +1 -1
- package/payload/platform/plugins/admin/skills/capabilities-here/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +91 -1
- package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +5 -1
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +13 -0
- package/payload/platform/plugins/docs/references/channel-wake-and-prompt.md +90 -0
- package/payload/platform/plugins/inventory/.claude-plugin/plugin.json +21 -0
- package/payload/platform/plugins/inventory/PLUGIN.md +75 -0
- package/payload/platform/plugins/inventory/lib/mcp-spawn-tee/index.js +204 -0
- package/payload/platform/plugins/inventory/lib/mcp-spawn-tee/package.json +3 -0
- package/payload/platform/plugins/inventory/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/inventory/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/index.js +241 -0
- package/payload/platform/plugins/inventory/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/forecast.d.ts +20 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/forecast.d.ts.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/forecast.js +29 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/forecast.js.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/neo4j.js +40 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/read.d.ts +28 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/read.d.ts.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/read.js +91 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/read.js.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/replenish.d.ts +56 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/replenish.d.ts.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/replenish.js +107 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/replenish.js.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/types.d.ts +98 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/types.d.ts.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/types.js +7 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/types.js.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/write-rejects.d.ts +12 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/write-rejects.d.ts.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/write-rejects.js +49 -0
- package/payload/platform/plugins/inventory/mcp/dist/lib/write-rejects.js.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/demand-record.d.ts +20 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/demand-record.d.ts.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/demand-record.js +53 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/demand-record.js.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/forecast.d.ts +23 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/forecast.d.ts.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/forecast.js +18 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/forecast.js.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/policy-set.d.ts +25 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/policy-set.d.ts.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/policy-set.js +37 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/policy-set.js.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/purchase-order-record.d.ts +25 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/purchase-order-record.d.ts.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/purchase-order-record.js +43 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/purchase-order-record.js.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/replenish-plan.d.ts +29 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/replenish-plan.d.ts.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/replenish-plan.js +95 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/replenish-plan.js.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/stock-record.d.ts +21 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/stock-record.d.ts.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/stock-record.js +46 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/stock-record.js.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/supplier-terms-set.d.ts +24 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/supplier-terms-set.d.ts.map +1 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/supplier-terms-set.js +29 -0
- package/payload/platform/plugins/inventory/mcp/dist/tools/supplier-terms-set.js.map +1 -0
- package/payload/platform/plugins/inventory/mcp/package.json +21 -0
- package/payload/platform/plugins/memory/references/schema-retail.md +20 -0
- package/payload/platform/scripts/lib/provision-account-dir.sh +0 -2
- package/payload/platform/services/claude-session-manager/dist/account-agent-registry.d.ts +19 -0
- package/payload/platform/services/claude-session-manager/dist/account-agent-registry.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/account-agent-registry.js +146 -0
- package/payload/platform/services/claude-session-manager/dist/account-agent-registry.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +8 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts +5 -0
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js +5 -5
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +11 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +124 -17
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +25 -13
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts +6 -1
- package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-census.js +6 -3
- package/payload/platform/services/claude-session-manager/dist/pty-census.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +21 -8
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +38 -15
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/public-spawn-model.d.ts +28 -0
- package/payload/platform/services/claude-session-manager/dist/public-spawn-model.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/public-spawn-model.js +58 -0
- package/payload/platform/services/claude-session-manager/dist/public-spawn-model.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +2 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +4 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/reaper.d.ts +3 -0
- package/payload/platform/services/claude-session-manager/dist/reaper.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/reaper.js +2 -1
- package/payload/platform/services/claude-session-manager/dist/reaper.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-cap-audit.d.ts +2 -0
- package/payload/platform/services/claude-session-manager/dist/session-cap-audit.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-cap-audit.js +5 -2
- package/payload/platform/services/claude-session-manager/dist/session-cap-audit.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts +2 -0
- package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js +5 -3
- package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts +26 -16
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.js +84 -31
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts +7 -0
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js +6 -0
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/notification.d.ts +10 -2
- package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/notification.js +15 -3
- package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/server.d.ts +5 -0
- package/payload/platform/services/telegram-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/server.js +57 -5
- package/payload/platform/services/telegram-channel/dist/server.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts +44 -2
- package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/turn-follow.js +188 -15
- package/payload/platform/services/telegram-channel/dist/turn-follow.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.d.ts +5 -0
- package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.js +53 -4
- package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts +44 -2
- package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/turn-follow.js +188 -15
- package/payload/platform/services/webchat-channel/dist/turn-follow.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/instructions.d.ts +9 -0
- package/payload/platform/services/whatsapp-channel/dist/instructions.d.ts.map +1 -0
- package/payload/platform/services/whatsapp-channel/dist/instructions.js +22 -0
- package/payload/platform/services/whatsapp-channel/dist/instructions.js.map +1 -0
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +3 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +10 -2
- package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.d.ts +10 -2
- package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.js +71 -8
- package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts +34 -2
- package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/turn-follow.js +153 -15
- package/payload/platform/services/whatsapp-channel/dist/turn-follow.js.map +1 -1
- package/payload/server/server.js +254 -96
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { fail, ok, } from "../lib/types.js";
|
|
2
|
+
export async function policySet(params) {
|
|
3
|
+
const { session, write, resolveProduct, resolveLocation, accountId, createdBy, sku, location, serviceLevel, reviewPeriodDays, } = params;
|
|
4
|
+
if (!(serviceLevel > 0 && serviceLevel < 1)) {
|
|
5
|
+
return fail(`[inventory] op=reject reason=service-level-out-of-range value=${serviceLevel} — must be strictly between 0 and 1. A service level of 1 needs infinite safety stock. Nothing was written.`);
|
|
6
|
+
}
|
|
7
|
+
if (!(reviewPeriodDays > 0)) {
|
|
8
|
+
return fail(`[inventory] op=reject reason=non-positive-review-period value=${reviewPeriodDays} — how many days between reorder reviews. Nothing was written.`);
|
|
9
|
+
}
|
|
10
|
+
const productNodeId = await resolveProduct(session, accountId, sku.trim());
|
|
11
|
+
if (productNodeId === null) {
|
|
12
|
+
return fail(`[inventory] op=reject reason=unknown-sku sku=${sku.trim()} — no product with that SKU on this account, or more than one. Nothing was written.`);
|
|
13
|
+
}
|
|
14
|
+
if (!(await resolveLocation(session, accountId, location.trim()))) {
|
|
15
|
+
return fail(`[inventory] op=reject reason=unknown-location location=${location.trim()} — no store location with that element id on this account. Nothing was written.`);
|
|
16
|
+
}
|
|
17
|
+
const relationships = [
|
|
18
|
+
{ type: "OF_PRODUCT", direction: "outgoing", targetNodeId: productNodeId },
|
|
19
|
+
{ type: "AT_LOCATION", direction: "outgoing", targetNodeId: location.trim() },
|
|
20
|
+
];
|
|
21
|
+
const result = await write({
|
|
22
|
+
session,
|
|
23
|
+
labels: ["ReplenishmentPolicy"],
|
|
24
|
+
props: {
|
|
25
|
+
accountId,
|
|
26
|
+
product: productNodeId,
|
|
27
|
+
location: location.trim(),
|
|
28
|
+
serviceLevel,
|
|
29
|
+
reviewPeriodDays,
|
|
30
|
+
},
|
|
31
|
+
relationships,
|
|
32
|
+
createdBy,
|
|
33
|
+
});
|
|
34
|
+
console.error(`[inventory] op=policy-set accountId=${accountId} sku=${sku.trim()} location=${location.trim()} serviceLevel=${serviceLevel} reviewPeriodDays=${reviewPeriodDays} nodesCreated=${result.nodesCreated ?? 0}`);
|
|
35
|
+
return ok(`Policy set for ${sku.trim()} at that location: ${(serviceLevel * 100).toFixed(1)}% service level, reviewed every ${reviewPeriodDays} days.`);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=policy-set.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-set.js","sourceRoot":"","sources":["../../src/tools/policy-set.ts"],"names":[],"mappings":"AAQA,OAAO,EAOL,IAAI,EACJ,EAAE,GACH,MAAM,iBAAiB,CAAC;AAiBzB,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAuB;IACrD,MAAM,EACJ,OAAO,EACP,KAAK,EACL,cAAc,EACd,eAAe,EACf,SAAS,EACT,SAAS,EACT,GAAG,EACH,QAAQ,EACR,YAAY,EACZ,gBAAgB,GACjB,GAAG,MAAM,CAAC;IAEX,IAAI,CAAC,CAAC,YAAY,GAAG,CAAC,IAAI,YAAY,GAAG,CAAC,CAAC,EAAE,CAAC;QAC5C,OAAO,IAAI,CACT,iEAAiE,YAAY,6GAA6G,CAC3L,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,CAAC,gBAAgB,GAAG,CAAC,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,CACT,iEAAiE,gBAAgB,gEAAgE,CAClJ,CAAC;IACJ,CAAC;IAED,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,qFAAqF,CAChJ,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,eAAe,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QAClE,OAAO,IAAI,CACT,0DAA0D,QAAQ,CAAC,IAAI,EAAE,iFAAiF,CAC3J,CAAC;IACJ,CAAC;IAED,MAAM,aAAa,GAAwB;QACzC,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE;QAC1E,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE;KAC9E,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC;QACzB,OAAO;QACP,MAAM,EAAE,CAAC,qBAAqB,CAAC;QAC/B,KAAK,EAAE;YACL,SAAS;YACT,OAAO,EAAE,aAAa;YACtB,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE;YACzB,YAAY;YACZ,gBAAgB;SACjB;QACD,aAAa;QACb,SAAS;KACV,CAAC,CAAC;IAEH,OAAO,CAAC,KAAK,CACX,uCAAuC,SAAS,QAAQ,GAAG,CAAC,IAAI,EAAE,aAAa,QAAQ,CAAC,IAAI,EAAE,iBAAiB,YAAY,qBAAqB,gBAAgB,iBAAiB,MAAM,CAAC,YAAY,IAAI,CAAC,EAAE,CAC5M,CAAC;IAEF,OAAO,EAAE,CACP,kBAAkB,GAAG,CAAC,IAAI,EAAE,sBAAsB,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,mCAAmC,gBAAgB,QAAQ,CAC7I,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 2448 — record a purchase order the operator confirmed.
|
|
3
|
+
*
|
|
4
|
+
* This records; it does not send. Sending is Task 2450 and is a different
|
|
5
|
+
* risk class. The reply says so explicitly, because an operator who believes
|
|
6
|
+
* the order went out will not send it themselves.
|
|
7
|
+
*/
|
|
8
|
+
import type { Session } from "neo4j-driver";
|
|
9
|
+
import { type CreatedBy, type ToolResult, type WriteFn } from "../lib/types.js";
|
|
10
|
+
export interface PurchaseOrderLine {
|
|
11
|
+
sku: string;
|
|
12
|
+
quantity: number;
|
|
13
|
+
}
|
|
14
|
+
export interface PurchaseOrderRecordParams {
|
|
15
|
+
session: Session;
|
|
16
|
+
write: WriteFn;
|
|
17
|
+
accountId: string;
|
|
18
|
+
createdBy: CreatedBy;
|
|
19
|
+
supplierNodeId: string;
|
|
20
|
+
orderDate: string;
|
|
21
|
+
expectedDelivery: string;
|
|
22
|
+
lines: PurchaseOrderLine[];
|
|
23
|
+
}
|
|
24
|
+
export declare function purchaseOrderRecord(params: PurchaseOrderRecordParams): Promise<ToolResult>;
|
|
25
|
+
//# sourceMappingURL=purchase-order-record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"purchase-order-record.d.ts","sourceRoot":"","sources":["../../src/tools/purchase-order-record.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EACL,KAAK,SAAS,EAEd,KAAK,UAAU,EACf,KAAK,OAAO,EAGb,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,iBAAiB,EAAE,CAAC;CAC5B;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,UAAU,CAAC,CAmErB"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { fail, ok, } from "../lib/types.js";
|
|
2
|
+
export async function purchaseOrderRecord(params) {
|
|
3
|
+
const { session, write, accountId, createdBy, supplierNodeId, orderDate, expectedDelivery, lines, } = params;
|
|
4
|
+
if (lines.length === 0) {
|
|
5
|
+
return fail(`[inventory] op=reject reason=empty-order accountId=${accountId} — an order with no lines is not an order. Nothing was written.`);
|
|
6
|
+
}
|
|
7
|
+
for (const line of lines) {
|
|
8
|
+
if (!(line.quantity > 0)) {
|
|
9
|
+
return fail(`[inventory] op=reject reason=non-positive-quantity sku=${line.sku} quantity=${line.quantity} — nothing was written.`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
if (!(Date.parse(expectedDelivery) >= Date.parse(orderDate))) {
|
|
13
|
+
return fail(`[inventory] op=reject reason=delivery-before-order orderDate=${orderDate} expectedDelivery=${expectedDelivery} — nothing was written.`);
|
|
14
|
+
}
|
|
15
|
+
if (supplierNodeId.trim() === "") {
|
|
16
|
+
return fail(`[inventory] op=reject reason=missing-supplier — pass the element id of the supplier organisation. Nothing was written.`);
|
|
17
|
+
}
|
|
18
|
+
// schema-retail.md requires lineItems on :PurchaseOrder. Neo4j has no map
|
|
19
|
+
// property type, so the lines are serialised to a JSON string rather than
|
|
20
|
+
// split across parallel arrays that could drift out of step.
|
|
21
|
+
const lineItems = JSON.stringify(lines);
|
|
22
|
+
const relationships = [
|
|
23
|
+
{ type: "FROM_SUPPLIER", direction: "outgoing", targetNodeId: supplierNodeId.trim() },
|
|
24
|
+
];
|
|
25
|
+
const result = await write({
|
|
26
|
+
session,
|
|
27
|
+
labels: ["PurchaseOrder"],
|
|
28
|
+
props: {
|
|
29
|
+
accountId,
|
|
30
|
+
supplier: supplierNodeId.trim(),
|
|
31
|
+
lineItems,
|
|
32
|
+
orderDate,
|
|
33
|
+
expectedDelivery,
|
|
34
|
+
orderStatus: "open",
|
|
35
|
+
},
|
|
36
|
+
relationships,
|
|
37
|
+
createdBy,
|
|
38
|
+
});
|
|
39
|
+
const units = lines.reduce((sum, l) => sum + l.quantity, 0);
|
|
40
|
+
console.error(`[inventory] op=purchase-order-record accountId=${accountId} supplier=${supplierNodeId.trim()} lines=${lines.length} units=${units} nodesCreated=${result.nodesCreated ?? 0} edgesCreated=${result.edgesCreated ?? 0}`);
|
|
41
|
+
return ok(`Recorded a purchase order for ${lines.length} line${lines.length === 1 ? "" : "s"}, ${units} units, expected ${expectedDelivery}. It has not been sent to the supplier: this records the order only.`);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=purchase-order-record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"purchase-order-record.js","sourceRoot":"","sources":["../../src/tools/purchase-order-record.ts"],"names":[],"mappings":"AAQA,OAAO,EAKL,IAAI,EACJ,EAAE,GACH,MAAM,iBAAiB,CAAC;AAkBzB,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAiC;IAEjC,MAAM,EACJ,OAAO,EACP,KAAK,EACL,SAAS,EACT,SAAS,EACT,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,KAAK,GACN,GAAG,MAAM,CAAC;IAEX,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CACT,sDAAsD,SAAS,iEAAiE,CACjI,CAAC;IACJ,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,EAAE,CAAC;YACzB,OAAO,IAAI,CACT,0DAA0D,IAAI,CAAC,GAAG,aAAa,IAAI,CAAC,QAAQ,yBAAyB,CACtH,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;QAC7D,OAAO,IAAI,CACT,gEAAgE,SAAS,qBAAqB,gBAAgB,yBAAyB,CACxI,CAAC;IACJ,CAAC;IACD,IAAI,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACjC,OAAO,IAAI,CACT,wHAAwH,CACzH,CAAC;IACJ,CAAC;IAED,0EAA0E;IAC1E,0EAA0E;IAC1E,6DAA6D;IAC7D,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAExC,MAAM,aAAa,GAAwB;QACzC,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,CAAC,IAAI,EAAE,EAAE;KACtF,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC;QACzB,OAAO;QACP,MAAM,EAAE,CAAC,eAAe,CAAC;QACzB,KAAK,EAAE;YACL,SAAS;YACT,QAAQ,EAAE,cAAc,CAAC,IAAI,EAAE;YAC/B,SAAS;YACT,SAAS;YACT,gBAAgB;YAChB,WAAW,EAAE,MAAM;SACpB;QACD,aAAa;QACb,SAAS;KACV,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC5D,OAAO,CAAC,KAAK,CACX,kDAAkD,SAAS,aAAa,cAAc,CAAC,IAAI,EAAE,UAAU,KAAK,CAAC,MAAM,UAAU,KAAK,iBAAiB,MAAM,CAAC,YAAY,IAAI,CAAC,iBAAiB,MAAM,CAAC,YAAY,IAAI,CAAC,EAAE,CACvN,CAAC;IAEF,OAAO,EAAE,CACP,iCAAiC,KAAK,CAAC,MAAM,QAAQ,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,oBAAoB,gBAAgB,sEAAsE,CACvM,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 2448 — what to reorder at one location, how much, and from whom.
|
|
3
|
+
*
|
|
4
|
+
* Two things this deliberately does not do. It never substitutes a default
|
|
5
|
+
* for a missing lead time, service level or history: those lines come back
|
|
6
|
+
* named as skipped, because a default would hand the operator a confident
|
|
7
|
+
* wrong quantity. And it never asserts the freshness of its own answer: the
|
|
8
|
+
* oldest and newest stock timestamps it actually read are returned, so the
|
|
9
|
+
* agent reports data age as a measured fact.
|
|
10
|
+
*/
|
|
11
|
+
import type { Session } from "neo4j-driver";
|
|
12
|
+
import { type PlanCandidate, type ToolResult } from "../lib/types.js";
|
|
13
|
+
export type ReadCandidatesFn = (session: Session, accountId: string, location: string) => Promise<PlanCandidate[]>;
|
|
14
|
+
export interface ReplenishPlanParams {
|
|
15
|
+
session: Session;
|
|
16
|
+
readCandidates: ReadCandidatesFn;
|
|
17
|
+
accountId: string;
|
|
18
|
+
location: string;
|
|
19
|
+
window: number;
|
|
20
|
+
periodDays: number;
|
|
21
|
+
/** ISO date used as the order date. Injected rather than read from the clock
|
|
22
|
+
* so the expected-arrival arithmetic is testable. */
|
|
23
|
+
today: string;
|
|
24
|
+
/** Recent refused writes, injected. Zero when the caller does not supply
|
|
25
|
+
* one, so the plan never fails because a log directory is unreadable. */
|
|
26
|
+
recentWriteRejects?: number;
|
|
27
|
+
}
|
|
28
|
+
export declare function replenishPlan(params: ReplenishPlanParams): Promise<ToolResult>;
|
|
29
|
+
//# sourceMappingURL=replenish-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replenish-plan.d.ts","sourceRoot":"","sources":["../../src/tools/replenish-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAQ5C,OAAO,EACL,KAAK,aAAa,EAGlB,KAAK,UAAU,EAEhB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,gBAAgB,GAAG,CAC7B,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;AAE9B,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,gBAAgB,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB;0DACsD;IACtD,KAAK,EAAE,MAAM,CAAC;IACd;8EAC0E;IAC1E,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAQD,wBAAsB,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CA2GpF"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { movingAverage } from "../lib/forecast.js";
|
|
2
|
+
import { daysOfCover, orderQuantity, reorderPoint, standardDeviation, } from "../lib/replenish.js";
|
|
3
|
+
import { ok, } from "../lib/types.js";
|
|
4
|
+
function addDays(isoDate, days) {
|
|
5
|
+
const d = new Date(`${isoDate}T00:00:00Z`);
|
|
6
|
+
d.setUTCDate(d.getUTCDate() + days);
|
|
7
|
+
return d.toISOString().slice(0, 10);
|
|
8
|
+
}
|
|
9
|
+
export async function replenishPlan(params) {
|
|
10
|
+
const { session, readCandidates, accountId, location, window, periodDays, today } = params;
|
|
11
|
+
const candidates = await readCandidates(session, accountId, location.trim());
|
|
12
|
+
const lines = [];
|
|
13
|
+
const skipped = [];
|
|
14
|
+
let asOfOldest = null;
|
|
15
|
+
let asOfNewest = null;
|
|
16
|
+
for (const c of candidates) {
|
|
17
|
+
if (asOfOldest === null || c.asOf < asOfOldest)
|
|
18
|
+
asOfOldest = c.asOf;
|
|
19
|
+
if (asOfNewest === null || c.asOf > asOfNewest)
|
|
20
|
+
asOfNewest = c.asOf;
|
|
21
|
+
if (c.history.length === 0) {
|
|
22
|
+
skipped.push({ sku: c.sku, location: c.location, missing: "history" });
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (c.serviceLevel === null || c.reviewPeriodDays === null) {
|
|
26
|
+
skipped.push({ sku: c.sku, location: c.location, missing: "serviceLevel" });
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (c.leadTimeDays === null ||
|
|
30
|
+
c.orderMultiple === null ||
|
|
31
|
+
c.minimumOrderQuantity === null ||
|
|
32
|
+
c.supplierNodeId === null) {
|
|
33
|
+
skipped.push({ sku: c.sku, location: c.location, missing: "leadTime" });
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const perPeriod = movingAverage(c.history, window);
|
|
37
|
+
const spread = standardDeviation(c.history.slice(-window));
|
|
38
|
+
const target = reorderPoint({
|
|
39
|
+
perPeriod,
|
|
40
|
+
stdDevPerPeriod: spread,
|
|
41
|
+
periodDays,
|
|
42
|
+
leadTimeDays: c.leadTimeDays,
|
|
43
|
+
reviewPeriodDays: c.reviewPeriodDays,
|
|
44
|
+
serviceLevel: c.serviceLevel,
|
|
45
|
+
});
|
|
46
|
+
const available = c.quantityOnHand + c.quantityOnOrder;
|
|
47
|
+
const recommended = orderQuantity({
|
|
48
|
+
target,
|
|
49
|
+
available,
|
|
50
|
+
minimumOrderQuantity: c.minimumOrderQuantity,
|
|
51
|
+
orderMultiple: c.orderMultiple,
|
|
52
|
+
});
|
|
53
|
+
if (recommended === 0)
|
|
54
|
+
continue;
|
|
55
|
+
lines.push({
|
|
56
|
+
sku: c.sku,
|
|
57
|
+
location: c.location,
|
|
58
|
+
daysOfCover: daysOfCover(c.quantityOnHand, perPeriod, periodDays),
|
|
59
|
+
reorderPoint: target,
|
|
60
|
+
recommendedQuantity: recommended,
|
|
61
|
+
supplierNodeId: c.supplierNodeId,
|
|
62
|
+
expectedArrival: addDays(today, c.leadTimeDays),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// Shortest cover first. A null cover means nothing is selling, which is the
|
|
66
|
+
// least urgent thing on the list, so it sorts last.
|
|
67
|
+
lines.sort((a, b) => {
|
|
68
|
+
if (a.daysOfCover === null)
|
|
69
|
+
return 1;
|
|
70
|
+
if (b.daysOfCover === null)
|
|
71
|
+
return -1;
|
|
72
|
+
return a.daysOfCover - b.daysOfCover;
|
|
73
|
+
});
|
|
74
|
+
console.error(`[inventory] op=plan accountId=${accountId} location=${location.trim()} lines=${lines.length} skipped=${skipped.length} asOfOldest=${asOfOldest ?? "-"} asOfNewest=${asOfNewest ?? "-"} recentWriteRejects=${params.recentWriteRejects ?? 0}`);
|
|
75
|
+
for (const s of skipped) {
|
|
76
|
+
console.error(`[inventory] op=plan-skip accountId=${accountId} sku=${s.sku} location=${s.location} missing=${s.missing}`);
|
|
77
|
+
}
|
|
78
|
+
const header = `op=plan lines=${lines.length} skipped=${skipped.length} asOfOldest=${asOfOldest ?? "-"} asOfNewest=${asOfNewest ?? "-"}`;
|
|
79
|
+
const body = lines
|
|
80
|
+
.map((l) => `${l.sku}: order ${l.recommendedQuantity}, cover ${l.daysOfCover === null ? "n/a" : l.daysOfCover.toFixed(1)} days, reorder point ${l.reorderPoint.toFixed(1)}, supplier ${l.supplierNodeId}, arrives ${l.expectedArrival}`)
|
|
81
|
+
.join("\n");
|
|
82
|
+
const skips = skipped
|
|
83
|
+
.map((s) => `${s.sku} at ${s.location}: missing=${s.missing}`)
|
|
84
|
+
.join("\n");
|
|
85
|
+
return ok([
|
|
86
|
+
header,
|
|
87
|
+
lines.length > 0 ? body : "Nothing needs ordering.",
|
|
88
|
+
skipped.length > 0
|
|
89
|
+
? `Could not be worked out:\n${skips}\nNo value was assumed for any of these.`
|
|
90
|
+
: "",
|
|
91
|
+
]
|
|
92
|
+
.filter((s) => s !== "")
|
|
93
|
+
.join("\n\n"));
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=replenish-plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replenish-plan.js","sourceRoot":"","sources":["../../src/tools/replenish-plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 2448 — record measured stock at a product-plus-location grain.
|
|
3
|
+
*
|
|
4
|
+
* One node per row. The tool resolves the SKU and confirms the location
|
|
5
|
+
* rather than creating either: a position pointing at a product that does not
|
|
6
|
+
* exist is worse than a rejected row, because the plan would silently omit it.
|
|
7
|
+
* Rejected rows are counted and named in the reply, never dropped quietly.
|
|
8
|
+
*/
|
|
9
|
+
import type { Session } from "neo4j-driver";
|
|
10
|
+
import { type CreatedBy, type ResolveLocationFn, type ResolveProductFn, type StockRow, type ToolResult, type WriteFn } from "../lib/types.js";
|
|
11
|
+
export interface StockRecordParams {
|
|
12
|
+
session: Session;
|
|
13
|
+
write: WriteFn;
|
|
14
|
+
resolveProduct: ResolveProductFn;
|
|
15
|
+
resolveLocation: ResolveLocationFn;
|
|
16
|
+
accountId: string;
|
|
17
|
+
createdBy: CreatedBy;
|
|
18
|
+
rows: StockRow[];
|
|
19
|
+
}
|
|
20
|
+
export declare function stockRecord(params: StockRecordParams): Promise<ToolResult>;
|
|
21
|
+
//# sourceMappingURL=stock-record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stock-record.d.ts","sourceRoot":"","sources":["../../src/tools/stock-record.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EACL,KAAK,SAAS,EAEd,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,OAAO,EAGb,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,iBAAiB;IAChC,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,QAAQ,EAAE,CAAC;CAClB;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CA2DhF"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { fail, ok, } from "../lib/types.js";
|
|
2
|
+
export async function stockRecord(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 stock 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
|
+
const productNodeId = await resolveProduct(session, accountId, sku);
|
|
13
|
+
if (productNodeId === null) {
|
|
14
|
+
rejected.push(`${sku} (unknown sku)`);
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const locationExists = await resolveLocation(session, accountId, location);
|
|
18
|
+
if (!locationExists) {
|
|
19
|
+
rejected.push(`${sku} (unknown location ${location})`);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const relationships = [
|
|
23
|
+
{ type: "OF_PRODUCT", direction: "outgoing", targetNodeId: productNodeId },
|
|
24
|
+
{ type: "AT_LOCATION", direction: "outgoing", targetNodeId: location },
|
|
25
|
+
];
|
|
26
|
+
await write({
|
|
27
|
+
session,
|
|
28
|
+
labels: ["StockPosition"],
|
|
29
|
+
props: {
|
|
30
|
+
accountId,
|
|
31
|
+
product: productNodeId,
|
|
32
|
+
location,
|
|
33
|
+
quantityOnHand: row.quantityOnHand,
|
|
34
|
+
quantityOnOrder: row.quantityOnOrder,
|
|
35
|
+
asOf: row.asOf,
|
|
36
|
+
},
|
|
37
|
+
relationships,
|
|
38
|
+
createdBy,
|
|
39
|
+
});
|
|
40
|
+
accepted += 1;
|
|
41
|
+
}
|
|
42
|
+
console.error(`[inventory] op=stock-record accountId=${accountId} rows=${rows.length} accepted=${accepted} rejected=${rejected.length}`);
|
|
43
|
+
const tail = rejected.length === 0 ? "" : ` Rejected: ${rejected.join("; ")}.`;
|
|
44
|
+
return ok(`op=stock-record rows=${rows.length} accepted=${accepted} rejected=${rejected.length}.${tail}`);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=stock-record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stock-record.js","sourceRoot":"","sources":["../../src/tools/stock-record.ts"],"names":[],"mappings":"AASA,OAAO,EAQL,IAAI,EACJ,EAAE,GACH,MAAM,iBAAiB,CAAC;AAYzB,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAyB;IACzD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,GACnF,MAAM,CAAC;IAET,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,IAAI,CACT,sDAAsD,SAAS,iDAAiD,CACjH,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QAErC,MAAM,aAAa,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;QACpE,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC,GAAG,GAAG,gBAAgB,CAAC,CAAC;YACtC,SAAS;QACX,CAAC;QAED,MAAM,cAAc,GAAG,MAAM,eAAe,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC3E,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,QAAQ,CAAC,IAAI,CAAC,GAAG,GAAG,sBAAsB,QAAQ,GAAG,CAAC,CAAC;YACvD,SAAS;QACX,CAAC;QAED,MAAM,aAAa,GAAwB;YACzC,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE;YAC1E,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE;SACvE,CAAC;QAEF,MAAM,KAAK,CAAC;YACV,OAAO;YACP,MAAM,EAAE,CAAC,eAAe,CAAC;YACzB,KAAK,EAAE;gBACL,SAAS;gBACT,OAAO,EAAE,aAAa;gBACtB,QAAQ;gBACR,cAAc,EAAE,GAAG,CAAC,cAAc;gBAClC,eAAe,EAAE,GAAG,CAAC,eAAe;gBACpC,IAAI,EAAE,GAAG,CAAC,IAAI;aACf;YACD,aAAa;YACb,SAAS;SACV,CAAC,CAAC;QACH,QAAQ,IAAI,CAAC,CAAC;IAChB,CAAC;IAED,OAAO,CAAC,KAAK,CACX,yCAAyC,SAAS,SAAS,IAAI,CAAC,MAAM,aAAa,QAAQ,aAAa,QAAQ,CAAC,MAAM,EAAE,CAC1H,CAAC;IAEF,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IAC/E,OAAO,EAAE,CACP,wBAAwB,IAAI,CAAC,MAAM,aAAa,QAAQ,aAAa,QAAQ,CAAC,MAAM,IAAI,IAAI,EAAE,CAC/F,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 2448 — put the commercial terms on the product-to-supplier edge.
|
|
3
|
+
*
|
|
4
|
+
* The edge must already exist. Creating it here would let a mistyped supplier
|
|
5
|
+
* id invent a supply relationship that the plan would then order against.
|
|
6
|
+
*/
|
|
7
|
+
import type { Session } from "neo4j-driver";
|
|
8
|
+
import type { SupplierTerms } from "../lib/read.js";
|
|
9
|
+
import { type ResolveProductFn, type ToolResult } from "../lib/types.js";
|
|
10
|
+
export type SetSupplierTermsFn = (session: Session, accountId: string, productNodeId: string, supplierNodeId: string, terms: SupplierTerms) => Promise<boolean>;
|
|
11
|
+
export interface SupplierTermsSetParams {
|
|
12
|
+
session: Session;
|
|
13
|
+
resolveProduct: ResolveProductFn;
|
|
14
|
+
setTerms: SetSupplierTermsFn;
|
|
15
|
+
accountId: string;
|
|
16
|
+
sku: string;
|
|
17
|
+
supplierNodeId: string;
|
|
18
|
+
leadTimeDays: number;
|
|
19
|
+
minimumOrderQuantity: number;
|
|
20
|
+
orderMultiple: number;
|
|
21
|
+
unitCost: number;
|
|
22
|
+
}
|
|
23
|
+
export declare function supplierTermsSet(params: SupplierTermsSetParams): Promise<ToolResult>;
|
|
24
|
+
//# sourceMappingURL=supplier-terms-set.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supplier-terms-set.d.ts","sourceRoot":"","sources":["../../src/tools/supplier-terms-set.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAY,MAAM,iBAAiB,CAAC;AAEnF,MAAM,MAAM,kBAAkB,GAAG,CAC/B,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,aAAa,KACjB,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,gBAAgB,CAAC;IACjC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CAAC,UAAU,CAAC,CAyDrB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { fail, ok } from "../lib/types.js";
|
|
2
|
+
export async function supplierTermsSet(params) {
|
|
3
|
+
const { session, resolveProduct, setTerms, accountId, sku, supplierNodeId, leadTimeDays, minimumOrderQuantity, orderMultiple, unitCost, } = params;
|
|
4
|
+
if (!(leadTimeDays >= 0)) {
|
|
5
|
+
return fail(`[inventory] op=reject reason=negative-lead-time value=${leadTimeDays} — lead time is whole days from order to arrival. Nothing was written.`);
|
|
6
|
+
}
|
|
7
|
+
if (!(orderMultiple > 0)) {
|
|
8
|
+
return fail(`[inventory] op=reject reason=non-positive-order-multiple value=${orderMultiple} — the pack size the supplier ships in. Use 1 when they ship singles. Nothing was written.`);
|
|
9
|
+
}
|
|
10
|
+
if (!(minimumOrderQuantity >= 0)) {
|
|
11
|
+
return fail(`[inventory] op=reject reason=negative-minimum-order value=${minimumOrderQuantity} — use 0 when the supplier has no minimum. Nothing was written.`);
|
|
12
|
+
}
|
|
13
|
+
const productNodeId = await resolveProduct(session, accountId, sku.trim());
|
|
14
|
+
if (productNodeId === null) {
|
|
15
|
+
return fail(`[inventory] op=reject reason=unknown-sku sku=${sku.trim()} — no product with that SKU on this account, or more than one. Nothing was written.`);
|
|
16
|
+
}
|
|
17
|
+
const applied = await setTerms(session, accountId, productNodeId, supplierNodeId.trim(), {
|
|
18
|
+
leadTimeDays,
|
|
19
|
+
minimumOrderQuantity,
|
|
20
|
+
orderMultiple,
|
|
21
|
+
unitCost,
|
|
22
|
+
});
|
|
23
|
+
if (!applied) {
|
|
24
|
+
return fail(`[inventory] op=reject reason=no-supplied-by-edge sku=${sku.trim()} supplier=${supplierNodeId.trim()} — link the product to the supplier first. This tool sets terms on an existing supply relationship, it does not create one. Nothing was written.`);
|
|
25
|
+
}
|
|
26
|
+
console.error(`[inventory] op=supplier-terms-set accountId=${accountId} sku=${sku.trim()} supplier=${supplierNodeId.trim()} leadTimeDays=${leadTimeDays} moq=${minimumOrderQuantity} multiple=${orderMultiple}`);
|
|
27
|
+
return ok(`Terms set for ${sku.trim()}: ${leadTimeDays} day lead time, minimum ${minimumOrderQuantity}, ordered in multiples of ${orderMultiple}, unit cost ${unitCost.toFixed(2)}.`);
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=supplier-terms-set.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supplier-terms-set.js","sourceRoot":"","sources":["../../src/tools/supplier-terms-set.ts"],"names":[],"mappings":"AAQA,OAAO,EAA0C,IAAI,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAuBnF,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAA8B;IAE9B,MAAM,EACJ,OAAO,EACP,cAAc,EACd,QAAQ,EACR,SAAS,EACT,GAAG,EACH,cAAc,EACd,YAAY,EACZ,oBAAoB,EACpB,aAAa,EACb,QAAQ,GACT,GAAG,MAAM,CAAC;IAEX,IAAI,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CACT,yDAAyD,YAAY,wEAAwE,CAC9I,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CACT,kEAAkE,aAAa,4FAA4F,CAC5K,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,CAAC,oBAAoB,IAAI,CAAC,CAAC,EAAE,CAAC;QACjC,OAAO,IAAI,CACT,6DAA6D,oBAAoB,iEAAiE,CACnJ,CAAC;IACJ,CAAC;IAED,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,qFAAqF,CAChJ,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,CAAC,IAAI,EAAE,EAAE;QACvF,YAAY;QACZ,oBAAoB;QACpB,aAAa;QACb,QAAQ;KACT,CAAC,CAAC;IAEH,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,IAAI,CACT,wDAAwD,GAAG,CAAC,IAAI,EAAE,aAAa,cAAc,CAAC,IAAI,EAAE,kJAAkJ,CACvP,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,KAAK,CACX,+CAA+C,SAAS,QAAQ,GAAG,CAAC,IAAI,EAAE,aAAa,cAAc,CAAC,IAAI,EAAE,iBAAiB,YAAY,QAAQ,oBAAoB,aAAa,aAAa,EAAE,CAClM,CAAC;IAEF,OAAO,EAAE,CACP,iBAAiB,GAAG,CAAC,IAAI,EAAE,KAAK,YAAY,2BAA2B,oBAAoB,6BAA6B,aAAa,eAAe,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAC3K,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@maxy/inventory",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "node dist/index.js",
|
|
10
|
+
"test": "vitest run"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
14
|
+
"neo4j-driver": "^5.28.1"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"typescript": "^5.7.0",
|
|
18
|
+
"@types/node": "^22.0.0",
|
|
19
|
+
"vitest": "^4.1.2"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -16,11 +16,22 @@ When loading this reference, confirm: "Using schema-base + schema-retail".
|
|
|
16
16
|
| StockMovement | `StockMovement` | `cdm:Activity` | `accountId`, `product` (id), `quantity`, `direction` (in/out), `reason`, `date` |
|
|
17
17
|
| PurchaseOrder | `PurchaseOrder` | `schema:Order` (subtype) | `accountId`, `supplier` (id), `lineItems`, `orderDate`, `expectedDelivery`, `orderStatus` |
|
|
18
18
|
| StoreLocation | `StoreLocation` | `schema:Store` | `accountId`, `name`, `address` (id), `openingHours` |
|
|
19
|
+
| StockPosition | `StockPosition` | platform-native | `accountId`, `product` (id), `location` (id), `quantityOnHand`, `quantityOnOrder`, `asOf` |
|
|
20
|
+
| DemandObservation | `DemandObservation` | platform-native | `accountId`, `product` (id), `location` (id), `periodStart`, `periodEnd`, `quantitySold` |
|
|
21
|
+
| ReplenishmentPolicy | `ReplenishmentPolicy` | platform-native | `accountId`, `product` (id), `location` (id), `serviceLevel`, `reviewPeriodDays` |
|
|
19
22
|
|
|
20
23
|
### PurchaseOrder vs base Order
|
|
21
24
|
|
|
22
25
|
`PurchaseOrder` is the retail-specific label for supplier-facing orders (restocking, procurement). Use `PurchaseOrder` (not `Order`) when ordering from a supplier. Customer-facing orders use the base `Order` label.
|
|
23
26
|
|
|
27
|
+
### StockPosition vs Product.stockLevel
|
|
28
|
+
|
|
29
|
+
`StockPosition` is the planning authority. It holds stock at a product-plus-location grain and carries an `asOf` timestamp, so a retailer with a shop and a warehouse can say how much sits where.
|
|
30
|
+
|
|
31
|
+
`Product.stockLevel` remains a required property and is untouched by this vertical's planning tools. No tool in the `inventory` plugin reads it. Treat it as a display convenience on the product record, not as a planning input.
|
|
32
|
+
|
|
33
|
+
`ReplenishmentPolicy` is deliberately a separate node from `StockPosition`: a position is a measured fact rewritten on every upload, a policy is an operator setting that changes rarely. On one node, every stock upload would have to preserve the policy or silently overwrite it.
|
|
34
|
+
|
|
24
35
|
---
|
|
25
36
|
|
|
26
37
|
## Relationship Patterns
|
|
@@ -30,4 +41,13 @@ When loading this reference, confirm: "Using schema-base + schema-retail".
|
|
|
30
41
|
(:Product)-[:SUPPLIED_BY]->(:Organization)
|
|
31
42
|
(:StockMovement)-[:AFFECTS]->(:Product)
|
|
32
43
|
(:PurchaseOrder)-[:FROM_SUPPLIER]->(:Organization)
|
|
44
|
+
(:StockPosition)-[:OF_PRODUCT]->(:Product)
|
|
45
|
+
(:StockPosition)-[:AT_LOCATION]->(:StoreLocation)
|
|
46
|
+
(:DemandObservation)-[:OF_PRODUCT]->(:Product)
|
|
47
|
+
(:DemandObservation)-[:AT_LOCATION]->(:StoreLocation)
|
|
48
|
+
(:ReplenishmentPolicy)-[:OF_PRODUCT]->(:Product)
|
|
49
|
+
(:ReplenishmentPolicy)-[:AT_LOCATION]->(:StoreLocation)
|
|
50
|
+
(:Product)-[:SUPPLIED_BY {leadTimeDays, minimumOrderQuantity, orderMultiple, unitCost}]->(:Organization)
|
|
33
51
|
```
|
|
52
|
+
|
|
53
|
+
`SUPPLIED_BY` carries the commercial terms because they are properties of the product-supplier pair, not of either node alone: the same product from two suppliers has two lead times. `leadTimeDays` is a whole number of days, `minimumOrderQuantity` and `orderMultiple` are in the product's selling unit, `unitCost` is in the account's currency.
|
|
@@ -420,8 +420,6 @@ for n in names:
|
|
|
420
420
|
echo " op=specialist-orphan-reconcile orphaned=$_orphaned"
|
|
421
421
|
fi
|
|
422
422
|
|
|
423
|
-
rm -f "$ACCOUNT_DIR/.claude/agents/"*.md 2>/dev/null || true
|
|
424
|
-
|
|
425
423
|
# SOUL.md — user-controlled. Only create if missing. Never overwrite.
|
|
426
424
|
if [ -f "$ACCOUNT_DIR/agents/admin/SOUL.md" ]; then
|
|
427
425
|
echo " agents/admin/SOUL.md preserved ($(wc -c < "$ACCOUNT_DIR/agents/admin/SOUL.md" | tr -d ' ') bytes)"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Two spawns in the same account sync concurrently — an admin session and a
|
|
2
|
+
* channel session are routine — and they write identical content. Whoever
|
|
3
|
+
* loses a link gets EEXIST on create or ENOENT on remove for work the winner
|
|
4
|
+
* has already done. Swallowing exactly those two codes keeps the loser going
|
|
5
|
+
* instead of aborting its remaining files and logging a failure that never
|
|
6
|
+
* happened, into the one surface this task exists to make trustworthy.
|
|
7
|
+
*
|
|
8
|
+
* Only the named code is swallowed. Every other errno propagates to the
|
|
9
|
+
* caller's own catch, which is what still turns a genuine EACCES or ENOTDIR
|
|
10
|
+
* into `op=account-agent-registry error`. Exported for its own unit test: the
|
|
11
|
+
* propagation half cannot be provoked through the filesystem from outside. */
|
|
12
|
+
export declare function ignoringRaceWith(code: 'EEXIST' | 'ENOENT', act: () => void): void;
|
|
13
|
+
/** Mirror `<accountDir>/specialists/agents/*.md` into
|
|
14
|
+
* `<accountDir>/.claude/agents/` as symlinks, and remove every registry
|
|
15
|
+
* symlink the source no longer has. Emits exactly one line:
|
|
16
|
+
* `op=account-agent-registry synced=<N> pruned=<N> collisions=<basenames>`,
|
|
17
|
+
* or one `op=account-agent-registry error reason=<msg>` if anything threw. */
|
|
18
|
+
export declare function syncAccountAgentRegistry(accountDir: string, log: (line: string) => void): void;
|
|
19
|
+
//# sourceMappingURL=account-agent-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-agent-registry.d.ts","sourceRoot":"","sources":["../src/account-agent-registry.ts"],"names":[],"mappings":"AA6BA;;;;;;;;;;+EAU+E;AAC/E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,EAAE,GAAG,EAAE,MAAM,IAAI,GAAG,IAAI,CAOjF;AAED;;;;+EAI+E;AAC/E,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAmF9F"}
|