@zeyos/client 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +75 -0
- package/README.md +16 -1
- package/agents/README.md +8 -0
- package/agents/shared/zeyos-agent-operating-guide.md +54 -3
- package/agents/shared/zeyos-entity-reference.md +5 -4
- package/agents/shared/zeyos-query-patterns.md +40 -1
- package/agents/zeyos/SKILL.md +50 -7
- package/agents/zeyos-account-intelligence/references/workflows.md +9 -3
- package/agents/zeyos-billing-insights/SKILL.md +1 -1
- package/agents/zeyos-billing-insights/references/workflows.md +26 -3
- package/agents/zeyos-calendar-and-scheduling/SKILL.md +45 -0
- package/agents/zeyos-calendar-and-scheduling/references/workflows.md +49 -0
- package/agents/zeyos-collections-and-dunning/SKILL.md +3 -1
- package/agents/zeyos-collections-and-dunning/references/workflows.md +7 -4
- package/agents/zeyos-data-quality-and-governance/SKILL.md +62 -0
- package/agents/zeyos-data-quality-and-governance/references/workflows.md +59 -0
- package/agents/zeyos-document-and-approval/SKILL.md +41 -0
- package/agents/zeyos-document-and-approval/references/workflows.md +43 -0
- package/agents/zeyos-mail-operations/SKILL.md +21 -0
- package/agents/zeyos-mail-operations/references/workflows.md +20 -0
- package/agents/zeyos-procurement-and-supplier-performance/SKILL.md +36 -0
- package/agents/zeyos-procurement-and-supplier-performance/references/workflows.md +46 -0
- package/agents/zeyos-time-tracking/SKILL.md +2 -0
- package/agents/zeyos-time-tracking/references/workflows.md +68 -0
- package/agents/zeyos-work-management/SKILL.md +4 -3
- package/agents/zeyos-work-management/references/workflows.md +63 -2
- package/docs/03-cli/02-commands.md +54 -2
- package/docs/03-cli/03-configuration.md +1 -0
- package/okf/concepts/calendar-timezones.md +10 -0
- package/okf/concepts/confirmation-and-side-effects.md +14 -0
- package/okf/concepts/currency-and-rounding.md +10 -0
- package/okf/concepts/idempotency-and-deduplication.md +10 -0
- package/okf/concepts/index.md +8 -0
- package/okf/concepts/null-empty-missing.md +10 -0
- package/okf/concepts/official-versus-latest.md +10 -0
- package/okf/concepts/ownership-versus-attention.md +15 -0
- package/okf/concepts/untrusted-business-content.md +10 -0
- package/okf/metrics/account-address-completeness.md +10 -0
- package/okf/metrics/index.md +3 -0
- package/okf/metrics/stock-movement-by-storage.md +10 -0
- package/okf/metrics/supplier-delivery-performance.md +10 -0
- package/okf/playbooks/activity-timeline.md +11 -0
- package/okf/playbooks/calendar-availability.md +11 -0
- package/okf/playbooks/campaign-recipient-coverage.md +12 -0
- package/okf/playbooks/document-approval.md +10 -0
- package/okf/playbooks/duplicate-account-review.md +11 -0
- package/okf/playbooks/effective-customer-price.md +11 -0
- package/okf/playbooks/index.md +8 -0
- package/okf/playbooks/missing-billing-addresses.md +12 -0
- package/okf/playbooks/supplier-scorecard.md +10 -0
- package/package.json +4 -2
- package/scripts/data/okf-curation.mjs +188 -0
- package/scripts/lib/live-test-config.mjs +20 -0
|
@@ -26,7 +26,9 @@ Typical prompts:
|
|
|
26
26
|
3. Use the correct primary resource:
|
|
27
27
|
- `transactions` for invoice and credit obligations
|
|
28
28
|
- `payments` for cash actually received
|
|
29
|
-
- `dunning` and `dunning2transactions` for reminder and notice state
|
|
29
|
+
- `dunning` and `dunning2transactions` for reminder and notice state. In the CLI, use
|
|
30
|
+
`zeyos count/list dunning` and `zeyos list dunning2transactions`; in `@zeyos/client`,
|
|
31
|
+
call `listDunningNotices` and `listDunningToTransactions`.
|
|
30
32
|
4. State what "overdue" means in the answer:
|
|
31
33
|
- past `duedate`
|
|
32
34
|
- unpaid or not fully settled
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## Primary Resources
|
|
4
4
|
|
|
5
|
-
The DB-table noun is not the operationId. Use the
|
|
6
|
-
|
|
5
|
+
The DB-table noun is not always the operationId. Use the CLI resource names below for
|
|
6
|
+
`zeyos count/list ...`; use the operationIds below with `@zeyos/client`
|
|
7
|
+
(`client.api.<operationId>(...)`). See [../../shared/zeyos-entity-reference.md](../../shared/zeyos-entity-reference.md#entity-noun-to-rest-operationid) for the full mapping.
|
|
7
8
|
|
|
8
9
|
| Concept | dbref noun | list operationId | single-record operationIds |
|
|
9
10
|
|---------|------------|------------------|----------------------------|
|
|
@@ -14,8 +15,10 @@ The DB-table noun is not the operationId. Use the operationIds below with `@zeyo
|
|
|
14
15
|
| Dunning-to-transaction links | `dunning2transactions` | `listDunningToTransactions` | `getDunningToTransaction`, `createDunningToTransaction`, `deleteDunningToTransaction` |
|
|
15
16
|
|
|
16
17
|
Note: `dunning` is the **`DunningNotice`** entity (not `Dunning`), and `dunning2transactions`
|
|
17
|
-
is the `DunningToTransaction` junction.
|
|
18
|
-
`
|
|
18
|
+
is the `DunningToTransaction` junction. The CLI maps those nouns directly, so
|
|
19
|
+
`zeyos count dunning` is the shortest path for a notice count. Calling
|
|
20
|
+
`client.api.listDunning(...)` or `client.api.listDunning2transactions(...)` will fail with
|
|
21
|
+
"operation not found"; use the operationIds in the table when writing JavaScript.
|
|
19
22
|
|
|
20
23
|
## Important Status Caution
|
|
21
24
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zeyos-data-quality-and-governance
|
|
3
|
+
description: Detect duplicate accounts, completeness gaps, stale data and schema hygiene issues in ZeyOS, and produce safe, explainable remediation previews. Use for "find duplicate customer accounts", "which customers are missing billing addresses", "show stale contacts with no email", "clean up duplicate records", "which custom fields are unused". Read-only by default — detection is separate from remediation, which requires a human decision per record.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ZeyOS Data Quality and Governance
|
|
7
|
+
|
|
8
|
+
Read [../shared/zeyos-agent-operating-guide.md](../shared/zeyos-agent-operating-guide.md) and [../shared/zeyos-query-patterns.md](../shared/zeyos-query-patterns.md) first. See the OKF `playbooks/duplicate-account-review` playbook and `concepts/null-empty-missing` concept.
|
|
9
|
+
Read [references/workflows.md](references/workflows.md) for concrete duplicate, anti-join,
|
|
10
|
+
and remediation-preview query plans.
|
|
11
|
+
|
|
12
|
+
> **Detection is not remediation.** Find and explain; never bulk-merge, archive or delete
|
|
13
|
+
> from a fuzzy match. Each fix is a human decision on a named ID.
|
|
14
|
+
|
|
15
|
+
Primary entities (cross-domain): `accounts`, `contacts`, `addresses`, `users`, `customfields`, `objects`, plus the domain records the user names.
|
|
16
|
+
|
|
17
|
+
Typical prompts:
|
|
18
|
+
|
|
19
|
+
- "Find duplicate customer accounts."
|
|
20
|
+
- "Which customers are missing billing addresses?"
|
|
21
|
+
- "Show stale contacts with no email."
|
|
22
|
+
- "Clean up duplicate records." (→ becomes a preview, not an action)
|
|
23
|
+
- "Which custom fields are unused or inconsistent?"
|
|
24
|
+
|
|
25
|
+
## Workflow
|
|
26
|
+
|
|
27
|
+
1. Define the population and the active/archived scope (R-012).
|
|
28
|
+
2. Normalize comparison fields (lowercase, trim) **without losing the original values** (R-020).
|
|
29
|
+
3. Generate candidate pairs from deterministic evidence and **score + explain** each:
|
|
30
|
+
- exact customer number → strong
|
|
31
|
+
- exact normalized email (incl. via `contacts`) → strong
|
|
32
|
+
- exact normalized name/address → strong
|
|
33
|
+
- near/fuzzy name only → weak (low confidence)
|
|
34
|
+
4. Sort candidates by score descending; label confidence high/medium/low.
|
|
35
|
+
5. Keep detection separate from remediation. For a "clean up" request, return a bounded
|
|
36
|
+
preview: exact IDs + proposed per-ID action, and request a human decision (R-009, R-023).
|
|
37
|
+
6. Re-query after any approved, bounded remediation.
|
|
38
|
+
|
|
39
|
+
## Fast Path: Missing Billing Addresses
|
|
40
|
+
|
|
41
|
+
For "active customers whose name starts with X and missing a billing address", use
|
|
42
|
+
`accounts.lastname` with the ZeyOS pattern operator. Do **not** invent `name`,
|
|
43
|
+
`name_starts`, or `lastname_starts` filters.
|
|
44
|
+
If the prompt already states that billing is address type `1`, shipping is type `0`, and
|
|
45
|
+
`addresses` has no visibility column, skip schema discovery and run the two list queries.
|
|
46
|
+
Batch address lookup with `account:[ids]`; do not loop one account at a time.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
zeyos list accounts --filter '{"type":1,"visibility":0,"lastname":{"~~*":"<prefix>%"}}' --fields ID,lastname --limit 1000 --json
|
|
50
|
+
zeyos list addresses --filter '{"account":[<accountIds>],"type":[0,1]}' --fields ID,account,type --limit 1000 --json
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`addresses` has no `visibility` column. Keep accounts with no type `1` address; derive
|
|
54
|
+
`has_shipping` from presence of a type `0` address.
|
|
55
|
+
|
|
56
|
+
## Safety
|
|
57
|
+
|
|
58
|
+
- Read-only by default.
|
|
59
|
+
- No automated merge until ZeyOS exposes a documented, reversible merge operation.
|
|
60
|
+
- Never bulk archive/delete from fuzzy matching (R-009, R-011).
|
|
61
|
+
- Never treat a shared generic email domain or similar name as conclusive.
|
|
62
|
+
- Preserve source IDs and explain confidence (R-020).
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Data Quality and Governance Workflows
|
|
2
|
+
|
|
3
|
+
## Duplicate-account detection (deterministic scoring)
|
|
4
|
+
|
|
5
|
+
1. Pull the active population:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
zeyos list accounts --filter '{"type":1,"visibility":0}' \
|
|
9
|
+
--fields ID,customernum,lastname,firstname --limit 10000 --json
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
2. Normalize comparison keys (lowercase, trim, collapse whitespace) but keep originals.
|
|
13
|
+
3. Score candidate pairs with a published, deterministic policy:
|
|
14
|
+
|
|
15
|
+
| Evidence | Weight | Confidence |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| Exact `customernum` | 1.0 | high |
|
|
18
|
+
| Exact normalized email (via `contacts`) | 0.9 | high |
|
|
19
|
+
| Exact normalized name + address | 0.8 | high |
|
|
20
|
+
| Near name only (edit distance) | 0.4 | low |
|
|
21
|
+
|
|
22
|
+
4. Sort pairs by score descending; emit `{accountA, accountB, score, reasons, confidence}`.
|
|
23
|
+
A clearly different account is not a candidate. Shared generic email domains and similar
|
|
24
|
+
names are never conclusive on their own.
|
|
25
|
+
|
|
26
|
+
## Completeness gaps (anti-join)
|
|
27
|
+
|
|
28
|
+
"Customers missing a billing address": list customers, list `addresses` of `type: 1`
|
|
29
|
+
(billing), keep customers with no matching `account`. `addresses` has **no** `visibility`
|
|
30
|
+
column — do not filter it. For scoped/prefix tasks, batch address lookup with
|
|
31
|
+
`account:[ids]`; do not loop one account at a time. Use:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
zeyos list accounts --filter '{"type":1,"visibility":0,"lastname":{"~~*":"<prefix>%"}}' --fields ID,lastname --limit 1000 --json
|
|
35
|
+
zeyos list addresses --filter '{"account":[<accountIds>],"type":[0,1]}' --fields ID,account,type --limit 1000 --json
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
State whether you treat empty string, null and missing the same (R-020); by default they
|
|
39
|
+
are distinct.
|
|
40
|
+
|
|
41
|
+
## Remediation is a preview, not an action
|
|
42
|
+
|
|
43
|
+
A "clean up duplicates" request returns:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{ "executed": false,
|
|
47
|
+
"candidates": [ { "accountA": 1, "accountB": 2 } ],
|
|
48
|
+
"proposedActions": [ { "accountId": 2, "action": "archive (needs human review)" } ] }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Never delete, archive or merge from this analysis (R-009, R-011, R-023). Each action is a
|
|
52
|
+
human decision on an exact ID. Re-query only after an approved, bounded change.
|
|
53
|
+
|
|
54
|
+
## Common failure modes
|
|
55
|
+
|
|
56
|
+
- Treating a fuzzy name match as a confirmed duplicate.
|
|
57
|
+
- Collapsing null / empty / zero into one bucket without saying so.
|
|
58
|
+
- Executing a bulk archive/delete from a "clean up" instruction.
|
|
59
|
+
- Filtering `visibility` on `addresses` (no such column → HTTP 400).
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zeyos-document-and-approval
|
|
3
|
+
description: Find formal ZeyOS documents, judge draft/final/obsolete status, run approval and finalization gates, and compare notes against formal SOPs. Use when status transitions, approval authority, version choice or formal file artifacts matter ("find the current approved onboarding SOP", "which contracts await approval", "make document 812 final", "compare the draft with the final version"). For lightweight note retrieval use zeyos-notes-and-sops.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ZeyOS Document and Approval
|
|
7
|
+
|
|
8
|
+
Read [../shared/zeyos-agent-operating-guide.md](../shared/zeyos-agent-operating-guide.md) and [../shared/zeyos-query-patterns.md](../shared/zeyos-query-patterns.md) first. See the OKF `concepts/official-versus-latest` concept and the `playbooks/document-approval` playbook.
|
|
9
|
+
|
|
10
|
+
> **Status, not freshness, decides authority.** The newest artifact is not necessarily the
|
|
11
|
+
> current official one. Retrieve content before claiming what a document says.
|
|
12
|
+
|
|
13
|
+
Primary entities: `documents` (`listDocuments`, `getDocument`, `createDocument`, `updateDocument`), `notes` (`listNotes`, `getNote`), `binfiles` (`listBinFiles`), `files`, plus `users`/`groups` for approval context. Document `status`: 0 DRAFT, 1 FEEDBACKREQUIRED, 2 INREVISION, 3 AWAITINGAPPROVAL, 4 FINAL, 5 OBSOLETE.
|
|
14
|
+
|
|
15
|
+
Typical prompts:
|
|
16
|
+
|
|
17
|
+
- "Find the current approved onboarding SOP."
|
|
18
|
+
- "Which contracts are awaiting approval?"
|
|
19
|
+
- "Compare the draft with the final version."
|
|
20
|
+
- "Make document 812 final."
|
|
21
|
+
|
|
22
|
+
## Workflow
|
|
23
|
+
|
|
24
|
+
1. Search formal `documents` first for "official", "approved", "final" or "current".
|
|
25
|
+
2. Select by **status plus freshness** — a FINAL document outranks a newer OBSOLETE one and a draft note (R-018).
|
|
26
|
+
3. Retrieve binary/file content (`binfile`/`files`) before claiming its contents.
|
|
27
|
+
4. Keep `notes` (lightweight) and `documents` (formal) as separate sources; do not silently merge conflicting sources (surface the conflict, name the authoritative source).
|
|
28
|
+
5. For finalization/approval: fetch the exact ID + current status, show a preview and any naming conflict, require exact confirmation, `updateDocument` one ID, then re-read and report old/new status (R-005, R-006, R-011).
|
|
29
|
+
|
|
30
|
+
## Routing boundaries
|
|
31
|
+
|
|
32
|
+
- Note-centric retrieval/summarization → `zeyos-notes-and-sops`.
|
|
33
|
+
- Use this skill when status transitions, approval authority, version choice or formal files matter.
|
|
34
|
+
|
|
35
|
+
## Safety
|
|
36
|
+
|
|
37
|
+
- FINAL, APPROVED, OBSOLETE and cancellation transitions are high impact (R-011).
|
|
38
|
+
- Never approve on behalf of an unidentified user/group.
|
|
39
|
+
- Never bulk-finalize or bulk-obsolete documents (R-009).
|
|
40
|
+
- Never overwrite a binary file without an explicit target and confirmation.
|
|
41
|
+
- Preserve provenance and revision identifiers in summaries.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Document and Approval Workflows
|
|
2
|
+
|
|
3
|
+
## Select the current official artifact
|
|
4
|
+
|
|
5
|
+
1. Query formal `documents` matching the topic; read `status`, `name`, `documentnum`, `filename`.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
zeyos list documents --filter '{"visibility":0,"name":{"~~*":"onboarding%"}}' \
|
|
9
|
+
--fields ID,name,status,filename,documentnum --limit 100 --json
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
2. Rank by authority, not date: prefer `status = 4` (FINAL). A newer `status = 5`
|
|
13
|
+
(OBSOLETE) does **not** win, and a draft `note` is only fallback context (R-018).
|
|
14
|
+
3. State the selection reason (which status, why it outranks the alternatives).
|
|
15
|
+
|
|
16
|
+
## Compare a note against a formal SOP
|
|
17
|
+
|
|
18
|
+
Retrieve the actual text of both (`notes.text`; `documents.description`/binary content).
|
|
19
|
+
Report agreements and conflicts explicitly; never synthesize a single answer that hides a
|
|
20
|
+
contradiction. The formal FINAL document is the authoritative source.
|
|
21
|
+
|
|
22
|
+
## Finalization / approval gate
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# 1. Fetch exact target + current status
|
|
26
|
+
zeyos get document <id> --json
|
|
27
|
+
# 2. Preview the change (no write) and surface any same-name conflict
|
|
28
|
+
zeyos update document <id> --query --status 4
|
|
29
|
+
# 3. Only after explicit confirmation of the exact ID:
|
|
30
|
+
zeyos update document <id> --status 4
|
|
31
|
+
# 4. Re-read and report old/new status
|
|
32
|
+
zeyos get document <id> --fields ID,name,status --json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Update exactly one ID. Never fuzzy-match a name to a set of documents and finalize them in
|
|
36
|
+
bulk (R-009, R-011). Leave similarly-named documents untouched.
|
|
37
|
+
|
|
38
|
+
## Common failure modes
|
|
39
|
+
|
|
40
|
+
- Picking the newest artifact instead of the FINAL one.
|
|
41
|
+
- Claiming document contents without retrieving the file/binary.
|
|
42
|
+
- Bulk-finalizing by fuzzy name match.
|
|
43
|
+
- Approving without identifying the approver / authority.
|
|
@@ -28,6 +28,27 @@ Typical prompts:
|
|
|
28
28
|
7. Treat textual drafts as safe. Treat message record creation/update as a write and sending or marking `mailbox=2` as high risk; require explicit confirmation plus verified sender context before any real mail mutation.
|
|
29
29
|
8. Escalate to `@zeyos/client` when you need binary content, MIME expansion, or richer message correlation than the CLI can express cleanly.
|
|
30
30
|
|
|
31
|
+
## Fast Path: Unanswered Inbox Count On Open Tickets
|
|
32
|
+
|
|
33
|
+
For "how many inbox messages (`mailbox` 0) are linked to open tickets and still
|
|
34
|
+
unanswered", use the workflow directly. This is a joined count, not a simple
|
|
35
|
+
`zeyos count messages` task.
|
|
36
|
+
|
|
37
|
+
If the prompt already states mailbox values and closed ticket statuses, skip schema
|
|
38
|
+
discovery. Do **not** use `notin`, `status_neq`, or `messageid` for this count.
|
|
39
|
+
Use the CLI commands below directly; do not write a scratch JavaScript client script for
|
|
40
|
+
this count because the CLI normalizes array filters to the API's native `IN` operator.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
zeyos list tickets --fields ID,status --filter '{"visibility":0,"status":[0,1,2,3,4,5,6,7,11]}' --limit 10000 --json
|
|
44
|
+
zeyos list messages --fields ID,ticket,reference,date --filter '{"mailbox":0,"ticket":[<ticketIds>]}' --limit 10000 --json
|
|
45
|
+
zeyos list messages --fields ID,ticket,reference,date --filter '{"mailbox":2,"ticket":[<ticketIds>]}' --limit 10000 --json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Count inbound rows where no sent row has the same `ticket`, `reference == inbound.ID`,
|
|
49
|
+
and `sent.date >= inbound.date`. Do not run a separate raw inbox count after listing the
|
|
50
|
+
rows; the join logic is the answer.
|
|
51
|
+
|
|
31
52
|
## Safety
|
|
32
53
|
|
|
33
54
|
- Never send email from an agent test or from a summary/draft request.
|
|
@@ -98,6 +98,26 @@ Recommended approach:
|
|
|
98
98
|
|
|
99
99
|
For an operational count, use this exact definition unless the user specifies another one: inbox message (`mailbox = 0`) linked to an open ticket, with no later sent message (`mailbox = 2`) whose `reference` points back to that inbound message.
|
|
100
100
|
|
|
101
|
+
Fast path for "how many inbox messages on open tickets are still unanswered":
|
|
102
|
+
|
|
103
|
+
1. If the prompt already states mailbox values and closed ticket statuses, skip schema
|
|
104
|
+
discovery. Do not use `notin`; open tickets are status `IN [0,1,2,3,4,5,6,7,11]`
|
|
105
|
+
with `visibility:0`.
|
|
106
|
+
Use the CLI commands below directly; do not write a scratch JavaScript client script
|
|
107
|
+
for this count because the CLI normalizes array filters to the API's native `IN`
|
|
108
|
+
operator.
|
|
109
|
+
2. Query open ticket IDs once:
|
|
110
|
+
`zeyos list tickets --fields ID,status --filter '{"visibility":0,"status":[0,1,2,3,4,5,6,7,11]}' --limit 10000 --json`
|
|
111
|
+
3. Query inbox and sent messages for those ticket IDs using batched `ticket:[ids]`:
|
|
112
|
+
`zeyos list messages --fields ID,ticket,reference,date --filter '{"mailbox":0,"ticket":[<ticketIds>]}' --limit 10000 --json`
|
|
113
|
+
`zeyos list messages --fields ID,ticket,reference,date --filter '{"mailbox":2,"ticket":[<ticketIds>]}' --limit 10000 --json`
|
|
114
|
+
4. Count inbound rows where no sent row has the same `ticket`, `reference == inbound.ID`,
|
|
115
|
+
and `sent.date >= inbound.date`. Do not run a separate `zeyos count messages` after
|
|
116
|
+
listing the rows; the join logic, not the raw inbox count, is the answer.
|
|
117
|
+
|
|
118
|
+
For this operational count, do not select `messageid`; `ID`, `ticket`, `reference`, and
|
|
119
|
+
`date` are sufficient, and some instances reject `messageid` in list field selection.
|
|
120
|
+
|
|
101
121
|
## Pattern: Draft A Reply
|
|
102
122
|
|
|
103
123
|
Use this for prompts like:
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zeyos-procurement-and-supplier-performance
|
|
3
|
+
description: Compare suppliers, analyze procurement orders/deliveries/invoices, lead times, price variance and reorder worklists in ZeyOS. Use for "which supplier is best for 20 units of item ABC", "which purchase orders are late", "compare supplier delivery performance", "where did procurement prices exceed the order". Read-only analysis — never place, book, cancel or transmit a procurement transaction.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ZeyOS Procurement and Supplier Performance
|
|
7
|
+
|
|
8
|
+
Read [../shared/zeyos-agent-operating-guide.md](../shared/zeyos-agent-operating-guide.md) and [../shared/zeyos-query-patterns.md](../shared/zeyos-query-patterns.md) first. See the OKF `playbooks/supplier-scorecard` playbook and `metrics/supplier-delivery-performance` metric.
|
|
9
|
+
|
|
10
|
+
> **Keep ordered, delivered and invoiced quantities/values distinct**, and state the
|
|
11
|
+
> ranking policy before you rank. Analysis is read-only.
|
|
12
|
+
|
|
13
|
+
Primary entities: `transactions` types **5 PROCUREMENT_REQUEST, 6 ORDER, 7 DELIVERY, 8 INVOICE, 9 CREDIT** (`listTransactions`), `suppliers` (item↔supplier links: `listSuppliers`), `items`, `accounts` (the supplier), `stocktransactions`, `storages`, `payments`, and `contracts` where supplier agreements matter. `transactions` has **no** `visibility` column.
|
|
14
|
+
|
|
15
|
+
Typical prompts:
|
|
16
|
+
|
|
17
|
+
- "Which supplier is best for 20 units of item ABC?"
|
|
18
|
+
- "Which purchase orders are late?"
|
|
19
|
+
- "Compare supplier delivery performance."
|
|
20
|
+
- "Where did procurement prices exceed the order?"
|
|
21
|
+
|
|
22
|
+
## Workflow
|
|
23
|
+
|
|
24
|
+
1. Resolve the item and the supplier `accounts` (type 2 SUPPLIER).
|
|
25
|
+
2. For sourcing decisions, read `suppliers` links: `price`, `minamount`, `deliverytime`, `stock`.
|
|
26
|
+
3. Apply the order quantity: a supplier is eligible only if `minamount <= quantity` — never hide a minimum-quantity constraint (R-003).
|
|
27
|
+
4. State the ranking policy first (e.g. eligible, then price ascending, then delivery time), then rank.
|
|
28
|
+
5. For performance, keep ordered (type 6), delivered (type 7) and invoiced (type 8) values separate; compute price variance as invoiced − ordered.
|
|
29
|
+
6. Compute only over a declared time window and one currency (R-014, R-019). Exclude cancelled records by documented status policy.
|
|
30
|
+
7. Label reorder advice as heuristic unless a formal stock policy exists.
|
|
31
|
+
|
|
32
|
+
## Safety
|
|
33
|
+
|
|
34
|
+
- Never place, approve, book, cancel or transmit a procurement transaction automatically (R-010, R-011).
|
|
35
|
+
- Never change supplier master data or price lists from an analytical request.
|
|
36
|
+
- Any future write workflow requires exact supplier, item, quantity, currency and confirmation.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Procurement and Supplier Performance Workflows
|
|
2
|
+
|
|
3
|
+
## Procurement transaction types
|
|
4
|
+
|
|
5
|
+
`transactions.type`: 5 PROCUREMENT_REQUEST, 6 PROCUREMENT_ORDER, 7 PROCUREMENT_DELIVERY,
|
|
6
|
+
8 PROCUREMENT_INVOICE, 9 PROCUREMENT_CREDIT. (Billing types 0–4 are a different domain.)
|
|
7
|
+
`transactions` has **no** `visibility` column — do not add `"visibility":0`.
|
|
8
|
+
|
|
9
|
+
## Supplier comparison for an order quantity
|
|
10
|
+
|
|
11
|
+
1. Resolve the item, then read its supplier links:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
zeyos list suppliers --filter '{"item":<itemId>}' \
|
|
15
|
+
--fields ID,account,price,minamount,deliverytime,stock --limit 1000 --json
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
2. Resolve each `account` to a supplier name.
|
|
19
|
+
3. Eligibility: `minamount <= orderQuantity`. State eligibility explicitly — never drop a
|
|
20
|
+
supplier silently because of its minimum.
|
|
21
|
+
4. Ranking policy (declare before ranking): eligible first, then `price` ascending, then
|
|
22
|
+
`deliverytime` ascending. Report `rank` and `eligible` per option.
|
|
23
|
+
|
|
24
|
+
## Supplier scorecard over a period
|
|
25
|
+
|
|
26
|
+
Group procurement `transactions` by supplier `account` within a declared window + currency:
|
|
27
|
+
|
|
28
|
+
- `ordered_value` = Σ `netamount` where type 6
|
|
29
|
+
- `invoiced_value` = Σ `netamount` where type 8
|
|
30
|
+
- `price_variance` = invoiced_value − ordered_value
|
|
31
|
+
- delivery timing from type 7 dates vs the order `duedate` (on-time when delivered by duedate)
|
|
32
|
+
|
|
33
|
+
Keep all values in one currency (R-019); exclude cancelled records per documented policy.
|
|
34
|
+
|
|
35
|
+
## Reorder advice
|
|
36
|
+
|
|
37
|
+
Label reorder suggestions as heuristic unless the instance has a formal stock policy
|
|
38
|
+
(min/max levels). Combine `stocktransactions` net-booked stock by `storages` with supplier
|
|
39
|
+
`deliverytime` to flag at-risk items — but present it as advice, not an action.
|
|
40
|
+
|
|
41
|
+
## Common failure modes
|
|
42
|
+
|
|
43
|
+
- Mixing billing types (0–4) with procurement types (5–9).
|
|
44
|
+
- Hiding a minimum-order-quantity constraint when ranking.
|
|
45
|
+
- Conflating ordered, delivered and invoiced values.
|
|
46
|
+
- Summing across currencies.
|
|
@@ -15,6 +15,7 @@ Typical prompts:
|
|
|
15
15
|
- "Record 2 hours on ticket 812." / "Book 90 minutes against Project Atlas."
|
|
16
16
|
- "Log half an hour for ACME, I was on a call about the renewal."
|
|
17
17
|
- "How much time did I log this week?" / "Summarize my logged hours by account."
|
|
18
|
+
- "Give me a summary of logged ticket time from the last four weeks."
|
|
18
19
|
- "Actually make that 90 minutes, not 60." / "Move that time to ticket 813."
|
|
19
20
|
|
|
20
21
|
## Two jobs
|
|
@@ -44,5 +45,6 @@ Interactivity here means **act first, then ask only when real data is ambiguous*
|
|
|
44
45
|
## Output discipline
|
|
45
46
|
|
|
46
47
|
- For "my work": state the resolved user and the open-status definition you used, then the list.
|
|
48
|
+
- For ticket time summaries: include both actionsteps directly linked by `actionstep.ticket` and actionsteps linked by `actionstep.task` where `task.ticket` is the summarized ticket; dedupe by actionstep ID before summing.
|
|
47
49
|
- For a logged entry: report the created actionstep id, the attached record (ticket/task/account), the effort in minutes, and the date.
|
|
48
50
|
- Separate what you resolved from what you assumed; call out any account or work-item ambiguity you had to break.
|
|
@@ -17,6 +17,7 @@ See [../../shared/zeyos-entity-reference.md](../../shared/zeyos-entity-reference
|
|
|
17
17
|
|
|
18
18
|
- **Time entries are `actionsteps`.** The `effort` field is **minutes** (integer). There is no separate time-entry resource.
|
|
19
19
|
- **Actionstep foreign keys:** `task`, `ticket`, `account`, `transaction` — plus `assigneduser`, `date`, `duedate`, `status`, `effort`. **There is no `project` FK on an actionstep**, so project-level time attaches through a ticket/task in that project, or to the project's `account`.
|
|
20
|
+
- **Ticket time rollups cross the task layer.** For "logged ticket time", count actionsteps with `ticket = <ticketId>` and actionsteps with `task = <taskId>` where that task has `ticket = <ticketId>`. Always dedupe by actionstep `ID` before summing in case a row carries both FKs.
|
|
20
21
|
- **Actionstep status:** `0` DRAFT · `1` COMPLETED · `2` CANCELLED · `3` BOOKED. Log already-done work as **COMPLETED (1)**; use **BOOKED (3)** only when the instance treats booked effort as the billed/locked record and the user says so.
|
|
21
22
|
- **Ticket/task status:** `0` NOT_STARTED · `1` AWAITING_ACCEPTANCE · `2` ACCEPTED · `3` REJECTED · `4` ACTIVE · `5` INACTIVE · `6` FEEDBACK_REQUIRED · `7` TESTING · `8` CANCELLED · `9` COMPLETED · `10` FAILED · `11` BOOKED. Treat **open/current = status `!IN [8,9,10,11]`** (exclude cancelled/completed/failed/booked). State this definition in the answer; the user can narrow it (e.g. only `4` ACTIVE).
|
|
22
23
|
- **Foreign keys per resource:** `tickets` carry `account`, `project`, `assigneduser`; `tasks` carry `ticket`, `project`, `assigneduser` (no `account` — reach the account through the task's ticket/project); `projects` carry `account`, `assigneduser`; `accounts` carry `lastname`, `firstname`, `customernum` (no `name` field).
|
|
@@ -195,6 +196,72 @@ const totalMinutes = rows.reduce((sum, r) => sum + (Number(r.effort) || 0), 0);
|
|
|
195
196
|
|
|
196
197
|
4. Sum `effort` as minutes; convert to hours only if asked. To break down "by account/ticket", group the rows by the FK and resolve the account/ticket names with a second query. Report the total and the breakdown, and state the window and status set you used.
|
|
197
198
|
|
|
199
|
+
## Pattern: "Give me a summary of logged ticket time from the last four weeks"
|
|
200
|
+
|
|
201
|
+
Use this whenever the user asks for ticket time, ticket hours, support time by ticket, or a ticket-level timesheet summary. A time entry can be attached directly to a ticket or indirectly to a task that belongs to a ticket; both are ticket time.
|
|
202
|
+
|
|
203
|
+
1. Resolve the date window to Unix seconds and count only `actionsteps.status IN [1,3]` unless the user explicitly asks for drafts/open follow-ups.
|
|
204
|
+
2. If the user named a specific ticket, resolve it first, then query:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
# direct ticket time
|
|
208
|
+
zeyos list actionsteps \
|
|
209
|
+
--fields ID,name,effort,date,status,ticket,task,assigneduser \
|
|
210
|
+
--filter '{"ticket":<ticketId>,"status":{"IN":[1,3]},"date":{">=":<start>,"<=":<end>}}' \
|
|
211
|
+
--limit 10000 --json
|
|
212
|
+
|
|
213
|
+
# tasks that belong to the ticket; do not filter task status for historical time
|
|
214
|
+
zeyos list tasks \
|
|
215
|
+
--fields ID,tasknum,name,ticket \
|
|
216
|
+
--filter '{"ticket":<ticketId>}' \
|
|
217
|
+
--limit 10000 --json
|
|
218
|
+
|
|
219
|
+
# time logged on those tasks
|
|
220
|
+
zeyos list actionsteps \
|
|
221
|
+
--fields ID,name,effort,date,status,ticket,task,assigneduser \
|
|
222
|
+
--filter '{"task":{"IN":[<taskIds>]},"status":{"IN":[1,3]},"date":{">=":<start>,"<=":<end>}}' \
|
|
223
|
+
--limit 10000 --json
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
3. If the user asks for all ticket time in a period, list actionsteps in the window with `ticket` and `task`, then resolve every referenced task to `task.ticket` and group by the resulting ticket ID. Page through results if the window is large.
|
|
227
|
+
4. Build each actionstep's ticket attribution:
|
|
228
|
+
- direct row: `actionstep.ticket`
|
|
229
|
+
- task row: `taskById[actionstep.task].ticket`
|
|
230
|
+
- both present: count the actionstep once; prefer the direct ticket if it conflicts and call out the conflict
|
|
231
|
+
- neither present: keep it out of the ticket-time total unless reporting an "unassigned/general time" bucket
|
|
232
|
+
5. Dedupe by actionstep `ID`, sum `effort` in minutes, and resolve ticket labels (`ticketnum`, `name`, `account.lastname`) for the summary.
|
|
233
|
+
|
|
234
|
+
Client shape for a specific ticket:
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
const [directRows, taskRows] = await Promise.all([
|
|
238
|
+
client.api.listActionSteps({
|
|
239
|
+
fields: ['ID', 'name', 'effort', 'date', 'status', 'ticket', 'task', 'assigneduser'],
|
|
240
|
+
filters: { ticket: ticketId, status: { IN: [1, 3] }, date: { '>=': start, '<=': end } },
|
|
241
|
+
limit: 10000,
|
|
242
|
+
}),
|
|
243
|
+
client.api.listTasks({
|
|
244
|
+
fields: ['ID', 'tasknum', 'name', 'ticket'],
|
|
245
|
+
filters: { ticket: ticketId },
|
|
246
|
+
limit: 10000,
|
|
247
|
+
}),
|
|
248
|
+
]);
|
|
249
|
+
|
|
250
|
+
const taskIds = taskRows.map((task) => task.ID);
|
|
251
|
+
const taskTimeRows = taskIds.length
|
|
252
|
+
? await client.api.listActionSteps({
|
|
253
|
+
fields: ['ID', 'name', 'effort', 'date', 'status', 'ticket', 'task', 'assigneduser'],
|
|
254
|
+
filters: { task: { IN: taskIds }, status: { IN: [1, 3] }, date: { '>=': start, '<=': end } },
|
|
255
|
+
limit: 10000,
|
|
256
|
+
})
|
|
257
|
+
: [];
|
|
258
|
+
|
|
259
|
+
const rowsById = new Map();
|
|
260
|
+
for (const row of [...directRows, ...taskTimeRows]) rowsById.set(String(row.ID), row);
|
|
261
|
+
const totalMinutes = [...rowsById.values()]
|
|
262
|
+
.reduce((sum, row) => sum + (Number(row.effort) || 0), 0);
|
|
263
|
+
```
|
|
264
|
+
|
|
198
265
|
## Pattern: Adjust or correct a logged entry
|
|
199
266
|
|
|
200
267
|
For "actually that was 90 minutes, not 60" or "move that time to ticket 813" right after logging — or any later correction.
|
|
@@ -226,5 +293,6 @@ Only correct entries the user pointed to (by id, or one you just created in this
|
|
|
226
293
|
- Filtering `visibility` on `transactions` (no such column -> HTTP 400).
|
|
227
294
|
- Putting effort in hours — `effort` is **minutes**.
|
|
228
295
|
- Trying to set a `project` FK on an actionstep — it does not exist; anchor on a ticket/task/account instead.
|
|
296
|
+
- Summing only `actionstep.ticket` for ticket time — time logged on tasks with `task.ticket = <ticketId>` belongs in the ticket total too.
|
|
229
297
|
- Treating BOOKED (status 11) tickets as current — they are terminal; the open set excludes `[8,9,10,11]`.
|
|
230
298
|
- Logging time against a user id you guessed — always read `sub` from `whoami`.
|
|
@@ -30,9 +30,10 @@ Typical prompts:
|
|
|
30
30
|
4. Follow relationships only after the primary record set is clear.
|
|
31
31
|
5. Treat "worked on" as a proxy unless actionstep effort/date evidence exists. Assignment and timestamps show involvement; `actionsteps.effort` on `COMPLETED` or `BOOKED` records is stronger time-entry evidence.
|
|
32
32
|
6. Distinguish direct project assignment from project inference through linked tickets.
|
|
33
|
-
7.
|
|
34
|
-
8.
|
|
35
|
-
9.
|
|
33
|
+
7. For logged ticket-time summaries, include actionsteps directly linked to the ticket and actionsteps linked to tasks whose `task.ticket` is that ticket; dedupe actionstep IDs before summing.
|
|
34
|
+
8. When the question is really about account or transaction follow-up, check `actionsteps` before inventing a new task.
|
|
35
|
+
9. For mutations, preview the affected record first and update with an explicit PATCH body.
|
|
36
|
+
10. Escalate from the CLI to `@zeyos/client` if the workflow needs unsupported joins, additional request control, or correlation across multiple list responses.
|
|
36
37
|
|
|
37
38
|
## Destructive Operations
|
|
38
39
|
|
|
@@ -155,11 +155,33 @@ For counts:
|
|
|
155
155
|
zeyos count actionsteps --filter '{"ticket":812,"status":0}' --json
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
+
For unanchored due-date counts, first narrow by equality filters and due-date presence
|
|
159
|
+
server-side. The API can return misleading zero counts for comparison filters like
|
|
160
|
+
`{"duedate":{"<=":4102444800}}`, and `duedate:null` is not a due item. If the
|
|
161
|
+
presence count is zero, the due-date count is zero.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
zeyos count actionsteps --filter '{"status":0,"duedate":{"!=":null}}' --json
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
If due dates exist, list those rows and count the timestamp range client-side.
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
zeyos list actionsteps \
|
|
171
|
+
--fields ID,status,duedate \
|
|
172
|
+
--filter '{"status":0,"duedate":{"!=":null}}' \
|
|
173
|
+
--limit 10000 \
|
|
174
|
+
--json
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Then parse the JSON and count rows where `duedate != null && Number(duedate) <= cutoff`.
|
|
178
|
+
|
|
158
179
|
## Pattern: Time Entry / Effort Summaries
|
|
159
180
|
|
|
160
181
|
Use this for prompts like:
|
|
161
182
|
|
|
162
183
|
- "How many minutes were booked on ticket 812 last week?"
|
|
184
|
+
- "Give me a summary of logged ticket time from the last four weeks."
|
|
163
185
|
- "Summarize completed actionstep effort for this account."
|
|
164
186
|
- "Which user logged effort against this project?"
|
|
165
187
|
|
|
@@ -168,8 +190,47 @@ Recommended approach:
|
|
|
168
190
|
1. Resolve the anchor and the date window.
|
|
169
191
|
2. Query `actionsteps` with fields `ID`, `date`, `status`, `effort`, and the relevant FK (`ticket`, `task`, `account`, or `transaction`).
|
|
170
192
|
3. Include statuses `1` COMPLETED and `3` BOOKED for time-entry totals unless the user asks for drafts/open follow-ups.
|
|
171
|
-
4.
|
|
172
|
-
|
|
193
|
+
4. For a simple ungrouped total, run `zeyos sum actionsteps effort --filter '{"status":[1,3]}'`.
|
|
194
|
+
For anchored or grouped totals, list the needed rows and sum `effort` as minutes.
|
|
195
|
+
Convert to hours only if the user asked for hours.
|
|
196
|
+
5. For ticket-level totals, roll task time up to the ticket:
|
|
197
|
+
- direct ticket time is `actionsteps.ticket = <ticketId>`
|
|
198
|
+
- task ticket time is `actionsteps.task = <taskId>` where `tasks.ticket = <ticketId>`
|
|
199
|
+
- fetch tasks by `ticket` for a named ticket, or resolve every referenced `actionstep.task` to `task.ticket` for a period-wide ticket summary
|
|
200
|
+
- do not filter tasks by status when resolving historical time; a completed task can still carry valid logged time
|
|
201
|
+
- dedupe by `actionsteps.ID` before summing in case a row has both `ticket` and `task`
|
|
202
|
+
6. Keep task/ticket assignment separate from effort totals.
|
|
203
|
+
|
|
204
|
+
Ticket-specific client example:
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
const [directRows, tasks] = await Promise.all([
|
|
208
|
+
client.api.listActionSteps({
|
|
209
|
+
fields: ['ID', 'date', 'status', 'effort', 'ticket', 'task', 'assigneduser'],
|
|
210
|
+
filters: { ticket: ticketId, status: { IN: [1, 3] }, date: { '>=': start, '<=': end } },
|
|
211
|
+
limit: 10000,
|
|
212
|
+
}),
|
|
213
|
+
client.api.listTasks({
|
|
214
|
+
fields: ['ID', 'ticket', 'name'],
|
|
215
|
+
filters: { ticket: ticketId },
|
|
216
|
+
limit: 10000,
|
|
217
|
+
}),
|
|
218
|
+
]);
|
|
219
|
+
|
|
220
|
+
const taskIds = tasks.map((task) => task.ID);
|
|
221
|
+
const taskRows = taskIds.length
|
|
222
|
+
? await client.api.listActionSteps({
|
|
223
|
+
fields: ['ID', 'date', 'status', 'effort', 'ticket', 'task', 'assigneduser'],
|
|
224
|
+
filters: { task: { IN: taskIds }, status: { IN: [1, 3] }, date: { '>=': start, '<=': end } },
|
|
225
|
+
limit: 10000,
|
|
226
|
+
})
|
|
227
|
+
: [];
|
|
228
|
+
|
|
229
|
+
const uniqueRows = new Map();
|
|
230
|
+
for (const row of [...directRows, ...taskRows]) uniqueRows.set(String(row.ID), row);
|
|
231
|
+
const totalMinutes = [...uniqueRows.values()]
|
|
232
|
+
.reduce((sum, row) => sum + (Number(row.effort) || 0), 0);
|
|
233
|
+
```
|
|
173
234
|
|
|
174
235
|
## Pattern: Create Follow-Up Work
|
|
175
236
|
|