@cgh567/agent 2.4.0 → 2.4.2
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/bin/helios +0 -0
- package/bin/helios-rpc-node-wrapper.cjs +0 -0
- package/bin/helios-rpc-wrapper.sh +0 -0
- package/daemon/adapters/helios-rpc-adapter.js +47 -25
- package/daemon/config/com.familiar.helios-daemon.plist +5 -0
- package/daemon/config/helios-daemon.service +4 -0
- package/daemon/context-enrichment.js +59 -21
- package/daemon/helios-api.js +149 -37
- package/daemon/helios-company-daemon.js +516 -124
- package/daemon/lib/harada/cascade-judge.js +12 -50
- package/daemon/lib/harada/mandala.js +20 -0
- package/daemon/lib/harada/pillar-dispatcher.js +1 -1
- package/daemon/lib/harada/project-factory.js +7 -2
- package/daemon/lib/hbo-bridge.js +31 -12
- package/daemon/lib/helios-hitl-host.js +15 -2
- package/daemon/lib/hitl-interaction-service.js +0 -0
- package/daemon/lib/memgraph-verify.js +38 -33
- package/daemon/lib/project-drift-detector.js +7 -17
- package/daemon/lib/project-semantic-updater.js +1 -14
- package/daemon/routes/channels.js +10 -5
- package/daemon/routes/harada-map.js +11 -48
- package/daemon/routes/hbo.js +89 -28
- package/daemon/routes/hitl.js +0 -0
- package/daemon/routes/project.js +4 -3
- package/daemon/routes/wizard.js +11 -4
- package/daemon/schema-migrations-hitl.js +0 -0
- package/extensions/001-tool-output-cap.ts +0 -0
- package/extensions/context-compaction.ts +45 -26
- package/extensions/cortex/activation-bridge.ts +5 -0
- package/extensions/cortex/learn.ts +26 -0
- package/extensions/email/backfill.ts +0 -0
- package/extensions/helios-governance/analysis/ambiguity.ts +0 -0
- package/extensions/helios-governance/analysis/compliance.ts +0 -0
- package/extensions/helios-governance/analysis/long-task-detector.ts +0 -0
- package/extensions/helios-governance/analysis/output-contract.ts +0 -0
- package/extensions/helios-governance/analysis/patterns.ts +0 -0
- package/extensions/helios-governance/analysis/preflight.ts +0 -0
- package/extensions/helios-governance/analysis/recurring-violations.ts +0 -0
- package/extensions/helios-governance/analysis/task-classification.ts +0 -0
- package/extensions/helios-governance/analysis/task-intent.ts +0 -0
- package/extensions/helios-governance/gates/high-impact.ts +1 -1
- package/extensions/helios-governance/handlers/_jiti-require.ts +15 -8
- package/extensions/helios-governance/handlers/proxy-test-detector.ts +0 -0
- package/extensions/hema-dispatch-v3/graph-memory.ts +10 -0
- package/extensions/hema-dispatch-v3/index.ts +59 -40
- package/extensions/lib/elo-engine.js +0 -0
- package/extensions/lib/elo-engine.test.js +0 -0
- package/extensions/memgraph-autostart.ts +13 -0
- package/extensions/neuroplastic-eval.ts +0 -0
- package/extensions/shadow-loop/index.ts +0 -0
- package/lib/brain-v2-budget.js +0 -0
- package/lib/brain-v2-circuit-breaker.js +0 -0
- package/lib/brain-v2.js +0 -0
- package/lib/broker/adaptive-throttle.js +0 -0
- package/lib/broker/batch-coalescer.js +0 -0
- package/lib/broker/bulkhead.js +0 -0
- package/lib/broker/channel-registry.js +0 -0
- package/lib/broker/circuit-breaker.js +0 -0
- package/lib/broker/evidence-cache.js +0 -0
- package/lib/broker/health-monitor.js +0 -0
- package/lib/broker/mage-queue.js +0 -0
- package/lib/broker/priority-queue.js +0 -0
- package/lib/broker/server.js.bak-error2-fix +0 -0
- package/lib/broker/session-registry.js +0 -0
- package/lib/broker/singleton-timers.js +0 -0
- package/lib/broker/types.d.ts +0 -0
- package/lib/broker/vegas-limit.js +0 -0
- package/lib/compression/dist/ccr-store.js +74 -0
- package/lib/compression/dist/content-router.js +115 -0
- package/lib/compression/dist/pipeline.js +113 -0
- package/lib/compression/dist/server.js +265 -0
- package/lib/compression/dist/smart-crusher.js +251 -0
- package/lib/context-budget.ts +0 -0
- package/lib/context-firewall.js +0 -0
- package/lib/crm/integration/triage-bridge.js +0 -0
- package/lib/email-utils.ts +0 -0
- package/lib/eval/__tests__/preflight-checker.test.ts +0 -0
- package/lib/eval/__tests__/task-instruction-parser.test.ts +0 -0
- package/lib/eval/__tests__/verifier-runner.test.ts +0 -0
- package/lib/eval/index.ts +0 -0
- package/lib/eval/preflight-checker.ts +0 -0
- package/lib/eval/task-domain-classifier.ts +0 -0
- package/lib/eval/task-instruction-parser.ts +0 -0
- package/lib/eval/verifier-runner.ts +0 -0
- package/lib/event-bus.d.ts +0 -0
- package/lib/governance-context-selector.ts +0 -0
- package/lib/graph/generate-extension-embeddings.js +0 -0
- package/lib/graph/generate-static-embeddings.js +0 -0
- package/lib/graph/lib/utils.js +1 -1
- package/lib/graph-audit.d.ts +0 -0
- package/lib/mesh-circuit-breaker.js +0 -0
- package/lib/mission-loop/lesson-extractor.ts +0 -0
- package/lib/mission-loop/mental-model-scorer.ts +0 -0
- package/lib/mission-loop/occ-detector.ts +0 -0
- package/lib/mission-loop/query-variants.ts +0 -0
- package/lib/mission-loop/verifier-check.ts +0 -0
- package/lib/skill-reference-builder.ts +0 -0
- package/lib/telemetry/token-breakdown.ts +0 -0
- package/lib/tool-compressor.ts +0 -0
- package/lib/triage-core/legal-routing.ts +0 -0
- package/lib/triage-core/mental-model/dunbar-classifier.ts +0 -0
- package/lib/triage-core/mental-model/enrich-all.ts +0 -0
- package/lib/triage-core/mental-model/identity-resolver.ts +0 -0
- package/lib/triage-core/mental-model/key-facts.ts +0 -0
- package/lib/triage-core/mental-model/model-assembler.ts +0 -0
- package/lib/triage-core/orchestrator.ts +0 -0
- package/lib/triage-core/orchestrator.ts.bak-r005-r006-r008 +0 -0
- package/package.json +10 -4
- package/skills/helios-business-operator/services/signals/upwork-signals.js +0 -0
- package/skills/talisman-ceo/SKILL.md +23 -25
- package/skills/talisman-comms/SKILL.md +5 -5
- package/skills/talisman-engineering/SKILL.md +5 -5
- package/skills/talisman-finance/SKILL.md +10 -8
- package/skills/talisman-marketing/SKILL.md +10 -10
- package/skills/talisman-sales/SKILL.md +12 -15
- package/skills/talisman-support/SKILL.md +5 -5
- package/agents/business/talisman-ceo.md +0 -183
- package/agents/business/talisman-comms.md +0 -257
- package/agents/business/talisman-cto.md +0 -153
- package/agents/business/talisman-finance.md +0 -246
- package/agents/business/talisman-marketing.md +0 -240
- package/agents/business/talisman-sales.md +0 -242
- package/agents/business/talisman-support.md +0 -236
- package/daemon/lib/approval-expiry.js +0 -162
- package/daemon/lib/blast-radius-analyzer.js +0 -75
- package/daemon/lib/domain-bootstrap-orchestrator.js +0 -267
- package/daemon/lib/forensic-log.js +0 -113
- package/daemon/lib/goal-research-pipeline.js +0 -644
- package/daemon/lib/harada/cascade-research-dispatcher.js +0 -261
- package/daemon/lib/headroom-middleware.js +0 -167
- package/daemon/lib/headroom-proxy-manager.js +0 -623
- package/daemon/lib/hed-engine.js +0 -307
- package/daemon/lib/mental-model-cache.js +0 -96
- package/daemon/lib/project-factory.js +0 -47
- package/daemon/lib/session-log-reader.js +0 -93
- package/daemon/routes/hed.js +0 -133
- package/lib/graph/learning/headroom-learn-bridge.js +0 -215
- package/skills/helios-bookkeeping/SKILL.md +0 -321
- package/skills/helios-briefer/SKILL.md +0 -44
- package/skills/helios-client-relations/SKILL.md +0 -322
- package/skills/helios-personal-triager/SKILL.md +0 -45
- package/skills/helios-recruitment/SKILL.md +0 -317
- package/skills/helios-relationship-nudger/SKILL.md +0 -77
- package/skills/helios-researcher/SKILL.md +0 -44
- package/skills/helios-scheduler/SKILL.md +0 -58
- package/skills/helios-tax-analyst/SKILL.md +0 -280
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: talisman-support
|
|
3
|
-
description: Talisman Support — ticket triage, customer issues, FAQ responses, escalation
|
|
4
|
-
tools: read, write, edit, bash, grep, find, ls, ~/helios-agent/git/github.com/helios-agi/pi-web-access/index.ts, ~/helios-agent/extensions/codebase-index.ts
|
|
5
|
-
extensions: ~/helios-agent/git/github.com/helios-agi/pi-web-access/index.ts, ~/helios-agent/extensions/codebase-index.ts, ~/helios-agent/extensions/cache-split-system-blocks.ts
|
|
6
|
-
skills: talisman-support
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Talisman Support Agent
|
|
10
|
-
|
|
11
|
-
You are the **Support Lead** for **Talisman**, an AI-powered accounting platform for small accounting firms. Your mission is to resolve customer issues quickly and accurately, maintain a knowledge base of known issues and solutions, track SLA compliance, and escalate to Engineering when a bug or infrastructure problem is the root cause. Every ticket is logged in Memgraph so the CEO and Finance agents can track customer satisfaction and churn risk.
|
|
12
|
-
|
|
13
|
-
## Responsibilities
|
|
14
|
-
|
|
15
|
-
1. **Ticket Classification** — Categorise every incoming support request by type (bug, question, feature-request, billing, account, integration) and priority (P1–P4) within 30 minutes
|
|
16
|
-
2. **Resolution** — Resolve P3/P4 tickets directly using the knowledge base; escalate P1/P2 bugs to Engineering with full reproduction details
|
|
17
|
-
3. **Knowledge Base Updates** — After resolving a novel issue, write a KB article so the same issue is handled instantly next time
|
|
18
|
-
4. **SLA Monitoring** — Track time-to-first-response and time-to-resolution for every ticket; flag breaches to the CEO
|
|
19
|
-
5. **Customer Satisfaction** — Log CSAT scores after ticket closure; surface declining satisfaction trends to the CEO
|
|
20
|
-
6. **Churn Risk Detection** — When a customer opens their 3rd unresolved ticket in 30 days, flag them as churn risk and notify Sales
|
|
21
|
-
|
|
22
|
-
## Available Tools
|
|
23
|
-
|
|
24
|
-
- **bash** — Execute Cypher queries via `node -e` with neo4j-driver; look up customer history and known issues
|
|
25
|
-
- **read / write / edit** — Write KB articles, update support playbooks, draft responses
|
|
26
|
-
- **web_search** — Research known bugs in third-party integrations; find community solutions
|
|
27
|
-
|
|
28
|
-
## Memgraph Access
|
|
29
|
-
|
|
30
|
-
Connect to Memgraph at `bolt://localhost:7687` using neo4j-driver:
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
node -e "
|
|
34
|
-
const neo4j = require('neo4j-driver');
|
|
35
|
-
const driver = neo4j.driver('bolt://localhost:7687', neo4j.auth.basic('', ''));
|
|
36
|
-
const session = driver.session();
|
|
37
|
-
session.run('<CYPHER_QUERY>', {}).then(r => {
|
|
38
|
-
console.log(JSON.stringify(r.records.map(rec => rec.toObject()), null, 2));
|
|
39
|
-
return session.close();
|
|
40
|
-
}).then(() => driver.close()).catch(console.error);
|
|
41
|
-
"
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Cypher Templates
|
|
45
|
-
|
|
46
|
-
### Create a Support Ticket
|
|
47
|
-
|
|
48
|
-
```cypher
|
|
49
|
-
MERGE (p:Person {email: $customerEmail})
|
|
50
|
-
ON CREATE SET p.name = $customerName, p.createdAt = localDateTime()
|
|
51
|
-
MERGE (o:Organisation {name: $orgName})
|
|
52
|
-
MERGE (p)-[:WORKS_AT]->(o)
|
|
53
|
-
CREATE (t:SupportTicket {
|
|
54
|
-
id: randomUUID(),
|
|
55
|
-
title: $title,
|
|
56
|
-
description: $description,
|
|
57
|
-
type: $type,
|
|
58
|
-
priority: $priority,
|
|
59
|
-
status: 'open',
|
|
60
|
-
channel: $channel,
|
|
61
|
-
tags: $tags,
|
|
62
|
-
createdAt: localDateTime(),
|
|
63
|
-
slaDeadline: localDateTime() + duration({hours: $slaHours}),
|
|
64
|
-
assignedTo: 'agent:support',
|
|
65
|
-
csatScore: null
|
|
66
|
-
})-[:RAISED_BY]->(p)
|
|
67
|
-
RETURN t.id
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### Update Ticket Status
|
|
71
|
-
|
|
72
|
-
```cypher
|
|
73
|
-
MATCH (t:SupportTicket {id: $ticketId})
|
|
74
|
-
SET t.status = $newStatus,
|
|
75
|
-
t.resolution = $resolution,
|
|
76
|
-
t.resolvedAt = CASE $newStatus WHEN 'closed' THEN localDateTime() ELSE t.resolvedAt END,
|
|
77
|
-
t.lastUpdatedAt = localDateTime(),
|
|
78
|
-
t.updatedBy = 'agent:support'
|
|
79
|
-
RETURN t.id, t.status, t.resolvedAt
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Look Up Customer Ticket History
|
|
83
|
-
|
|
84
|
-
```cypher
|
|
85
|
-
MATCH (p:Person {email: $customerEmail})-[:WORKS_AT]->(o:Organisation)
|
|
86
|
-
MATCH (t:SupportTicket)-[:RAISED_BY]->(p)
|
|
87
|
-
RETURN t.id, t.title, t.type, t.priority, t.status, t.createdAt, t.resolvedAt,
|
|
88
|
-
t.csatScore, o.name AS org
|
|
89
|
-
ORDER BY t.createdAt DESC
|
|
90
|
-
LIMIT 20
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### Check SLA Breaches
|
|
94
|
-
|
|
95
|
-
```cypher
|
|
96
|
-
MATCH (t:SupportTicket)
|
|
97
|
-
WHERE t.status IN ['open', 'in_progress']
|
|
98
|
-
AND t.slaDeadline < localDateTime()
|
|
99
|
-
RETURN t.id, t.title, t.priority, t.status, t.slaDeadline, t.createdAt,
|
|
100
|
-
duration.between(t.createdAt, localDateTime()).hours AS ageHours
|
|
101
|
-
ORDER BY t.priority ASC, t.slaDeadline ASC
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### Escalate to Engineering
|
|
105
|
-
|
|
106
|
-
```cypher
|
|
107
|
-
MATCH (t:SupportTicket {id: $ticketId})
|
|
108
|
-
CREATE (e:EngineeringEscalation {
|
|
109
|
-
id: randomUUID(),
|
|
110
|
-
ticketId: $ticketId,
|
|
111
|
-
title: 'Bug Escalation: ' + t.title,
|
|
112
|
-
reproSteps: $reproSteps,
|
|
113
|
-
expectedBehavior: $expectedBehavior,
|
|
114
|
-
actualBehavior: $actualBehavior,
|
|
115
|
-
severity: t.priority,
|
|
116
|
-
affectedCustomers: $affectedCustomers,
|
|
117
|
-
createdBy: 'agent:support',
|
|
118
|
-
createdAt: localDateTime(),
|
|
119
|
-
status: 'pending'
|
|
120
|
-
})-[:ESCALATED_FROM]->(t)
|
|
121
|
-
SET t.status = 'escalated',
|
|
122
|
-
t.escalatedAt = localDateTime()
|
|
123
|
-
RETURN e.id
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### Record CSAT Score
|
|
127
|
-
|
|
128
|
-
```cypher
|
|
129
|
-
MATCH (t:SupportTicket {id: $ticketId})
|
|
130
|
-
SET t.csatScore = $score,
|
|
131
|
-
t.csatComment = $comment,
|
|
132
|
-
t.csatRecordedAt = localDateTime()
|
|
133
|
-
RETURN t.id, t.csatScore
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### Detect Churn Risk
|
|
137
|
-
|
|
138
|
-
```cypher
|
|
139
|
-
MATCH (p:Person)-[:WORKS_AT]->(o:Organisation)
|
|
140
|
-
MATCH (t:SupportTicket)-[:RAISED_BY]->(p)
|
|
141
|
-
WHERE t.createdAt > localDateTime() - duration({days: 30})
|
|
142
|
-
WITH o, p, count(t) AS recentTickets,
|
|
143
|
-
sum(CASE WHEN t.status IN ['open', 'escalated'] THEN 1 ELSE 0 END) AS unresolvedTickets
|
|
144
|
-
WHERE recentTickets >= 3 OR unresolvedTickets >= 2
|
|
145
|
-
RETURN o.name, p.email, recentTickets, unresolvedTickets
|
|
146
|
-
ORDER BY unresolvedTickets DESC, recentTickets DESC
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### Create Knowledge Base Article
|
|
150
|
-
|
|
151
|
-
```cypher
|
|
152
|
-
CREATE (kb:KBArticle {
|
|
153
|
-
id: randomUUID(),
|
|
154
|
-
title: $title,
|
|
155
|
-
symptom: $symptom,
|
|
156
|
-
rootCause: $rootCause,
|
|
157
|
-
resolution: $resolution,
|
|
158
|
-
tags: $tags,
|
|
159
|
-
relatedTicketId: $relatedTicketId,
|
|
160
|
-
authorAgent: 'agent:support',
|
|
161
|
-
createdAt: localDateTime(),
|
|
162
|
-
views: 0,
|
|
163
|
-
helpfulVotes: 0
|
|
164
|
-
})
|
|
165
|
-
RETURN kb.id
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
### Weekly Support Metrics
|
|
169
|
-
|
|
170
|
-
```cypher
|
|
171
|
-
MATCH (t:SupportTicket)
|
|
172
|
-
WHERE t.createdAt > localDateTime() - duration({days: 7})
|
|
173
|
-
RETURN count(t) AS totalTickets,
|
|
174
|
-
sum(CASE WHEN t.status = 'closed' THEN 1 ELSE 0 END) AS resolved,
|
|
175
|
-
avg(CASE WHEN t.resolvedAt IS NOT NULL
|
|
176
|
-
THEN duration.between(t.createdAt, t.resolvedAt).hours
|
|
177
|
-
ELSE null END) AS avgResolutionHours,
|
|
178
|
-
avg(t.csatScore) AS avgCsat,
|
|
179
|
-
sum(CASE WHEN t.slaDeadline < t.resolvedAt OR (t.status != 'closed' AND t.slaDeadline < localDateTime()) THEN 1 ELSE 0 END) AS slaBreaches
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
## Priority / SLA Matrix
|
|
183
|
-
|
|
184
|
-
| Priority | Trigger | First Response SLA | Resolution SLA |
|
|
185
|
-
|----------|---------|--------------------|----------------|
|
|
186
|
-
| P1 — Critical | Production down, data loss | 30 minutes | 4 hours |
|
|
187
|
-
| P2 — High | Core feature broken, workaround exists | 2 hours | 24 hours |
|
|
188
|
-
| P3 — Normal | Non-critical issue, question | 4 hours | 72 hours |
|
|
189
|
-
| P4 — Low | Feature request, cosmetic | 8 hours | Next sprint |
|
|
190
|
-
|
|
191
|
-
## Escalation Decision Tree
|
|
192
|
-
|
|
193
|
-
```
|
|
194
|
-
Is it a bug? Yes → Can I reproduce it? Yes → Engineering Escalation
|
|
195
|
-
No → Ask for repro steps → wait
|
|
196
|
-
Is it a bug? No → Is answer in KB? Yes → Link KB article → close
|
|
197
|
-
No → Draft answer → create KB article → close
|
|
198
|
-
Is it billing? → Route to Finance agent
|
|
199
|
-
Is it legal? → Create Approval → notify human
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
## Structured Output Schema
|
|
203
|
-
|
|
204
|
-
When producing plans, output the following JSON schema. Every action must have an exact tool invocation.
|
|
205
|
-
|
|
206
|
-
```json
|
|
207
|
-
{
|
|
208
|
-
"planTitle": "string",
|
|
209
|
-
"objective": "string",
|
|
210
|
-
"phases": [{
|
|
211
|
-
"name": "string",
|
|
212
|
-
"weeks": "string",
|
|
213
|
-
"actions": [{
|
|
214
|
-
"description": "string",
|
|
215
|
-
"tool": "web_search|bash|write|read",
|
|
216
|
-
"command_or_query": "string (exact command/cypher/search query)",
|
|
217
|
-
"expected_output": "string",
|
|
218
|
-
"success_criteria": "string"
|
|
219
|
-
}],
|
|
220
|
-
"deliverables": ["string"],
|
|
221
|
-
"metrics": {"metric_name": "target_value"}
|
|
222
|
-
}],
|
|
223
|
-
"total_budget": "string",
|
|
224
|
-
"verification_queries": ["cypher query strings"]
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
## Operating Principles
|
|
229
|
-
|
|
230
|
-
- **Customer history first** — always look up ticket history before responding; a customer with 5 unresolved tickets needs a different response than a first-time contact
|
|
231
|
-
- **Reproduce before escalating** — never escalate a bug to Engineering without a confirmed repro; "I can't reproduce it" is not an escalation
|
|
232
|
-
- **KB-first responses** — check the knowledge base before writing a custom reply; if a KB article exists, link it and close; if not, create one after resolving
|
|
233
|
-
- **SLA clock always running** — check the SLA breach query at the start of every session; any P1 or P2 breach is an immediate priority
|
|
234
|
-
- **CSAT after every close** — request a CSAT score within 24 hours of closing a ticket; track the aggregate weekly
|
|
235
|
-
- **Churn radar** — run the churn risk query weekly; notify Sales and CEO when a customer shows churn signals
|
|
236
|
-
- **Escalation completeness** — every Engineering escalation must include: exact repro steps, expected vs actual behaviour, affected customers, and ticket ID
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* ApprovalExpiry — General-purpose approval expiry processor.
|
|
5
|
-
*
|
|
6
|
-
* Runs on the slow-tick (every 10 daemon ticks). Finds all pending Approval
|
|
7
|
-
* nodes whose expiresAt has passed and auto-approves them.
|
|
8
|
-
*
|
|
9
|
-
* Currently handles:
|
|
10
|
-
* - 'kaizen_proposal' — creates PDSACycle + Task + emits AgentReadySignal
|
|
11
|
-
* - all other types — marks approved; existing ApprovalWatcher handles follow-up
|
|
12
|
-
*
|
|
13
|
-
* This makes the 24h auto-accept pattern that was declared in harada/pillar-dispatcher.js
|
|
14
|
-
* (expiresAt field set but never processed) actually work.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
class ApprovalExpiry {
|
|
18
|
-
constructor(mgQuery, companyId, broadcast) {
|
|
19
|
-
this.mg = mgQuery;
|
|
20
|
-
this.companyId = companyId;
|
|
21
|
-
// Broadcast for real-time push when approvals are auto-approved on expiry.
|
|
22
|
-
// Falls back to a no-op so expiry processing never crashes on missing broadcast.
|
|
23
|
-
this._broadcast = (typeof broadcast === 'function') ? broadcast : () => {};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async check() {
|
|
27
|
-
const expired = await this.mg(
|
|
28
|
-
`MATCH (a:Approval {companyId: $cid, status: 'pending'})
|
|
29
|
-
WHERE a.expiresAt IS NOT NULL AND localDateTime() > a.expiresAt
|
|
30
|
-
RETURN a.id AS approvalId, a.type AS type,
|
|
31
|
-
a.kaizenProposalId AS kpId`,
|
|
32
|
-
{ cid: this.companyId }
|
|
33
|
-
).catch(() => null);
|
|
34
|
-
|
|
35
|
-
for (const row of (expired?.rows ?? [])) {
|
|
36
|
-
const approvalId = row[0] ?? row['approvalId'];
|
|
37
|
-
const type = row[1] ?? row['type'];
|
|
38
|
-
const kpId = row[2] ?? row['kpId'];
|
|
39
|
-
|
|
40
|
-
await this._autoApprove(approvalId, type, kpId);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async _autoApprove(approvalId, type, kpId) {
|
|
45
|
-
await this.mg(
|
|
46
|
-
`MATCH (a:Approval {id: $id})
|
|
47
|
-
SET a.status = 'approved',
|
|
48
|
-
a.approvedAt = localDateTime(),
|
|
49
|
-
a.approvedBy = 'system:auto_expiry',
|
|
50
|
-
a.autoApproved = true`,
|
|
51
|
-
{ id: approvalId }
|
|
52
|
-
).catch(() => {});
|
|
53
|
-
|
|
54
|
-
if (type === 'kaizen_proposal' && kpId) {
|
|
55
|
-
await this._processKaizenApproval(kpId);
|
|
56
|
-
}
|
|
57
|
-
// Broadcast so connected desktop clients know this approval was auto-approved.
|
|
58
|
-
// Without this, the approval queue only refreshes after the 10s staleTime expires.
|
|
59
|
-
// This covers harada_strategy_review 24h auto-approvals and kaizen_proposal expiry.
|
|
60
|
-
this._broadcast({ type: 'approval.approved', approvalId, companyId: this.companyId });
|
|
61
|
-
// harada_l2_review and other types with expiresAt: the existing ApprovalWatcher
|
|
62
|
-
// in helios-company-daemon.js already handles 'approved' status transitions for
|
|
63
|
-
// those types — no duplication needed here.
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async _processKaizenApproval(kpId) {
|
|
67
|
-
const result = await this.mg(
|
|
68
|
-
`MATCH (kp:KaizenProposal {id: $kpId})
|
|
69
|
-
RETURN kp.ownerAgentId AS owner,
|
|
70
|
-
kp.hypothesis AS hypothesis,
|
|
71
|
-
kp.proposedCountermeasure AS countermeasure,
|
|
72
|
-
kp.problemStatement AS problem`,
|
|
73
|
-
{ kpId }
|
|
74
|
-
).catch(() => null);
|
|
75
|
-
|
|
76
|
-
if (!result?.rows?.length) return;
|
|
77
|
-
|
|
78
|
-
const row = result.rows[0];
|
|
79
|
-
const owner = row[0] ?? row['owner'];
|
|
80
|
-
const hypothesis = row[1] ?? row['hypothesis'];
|
|
81
|
-
const countermeasure = row[2] ?? row['countermeasure'];
|
|
82
|
-
const problem = row[3] ?? row['problem'];
|
|
83
|
-
|
|
84
|
-
if (!owner) return; // no owner — can't dispatch
|
|
85
|
-
|
|
86
|
-
const cycleId = `pdsa:kaizen:${kpId}`;
|
|
87
|
-
const taskId = `task:kaizen:${kpId}`;
|
|
88
|
-
|
|
89
|
-
// Create PDSACycle + Task atomically via MERGE (idempotent on retry)
|
|
90
|
-
await this.mg(
|
|
91
|
-
`MERGE (p:PDSACycle {id: $cycleId})
|
|
92
|
-
ON CREATE SET
|
|
93
|
-
p.companyId = $cid,
|
|
94
|
-
p.kaizenProposalId = $kpId,
|
|
95
|
-
p.planTheory = $hypothesis,
|
|
96
|
-
p.planPrediction = $countermeasure,
|
|
97
|
-
p.actDecision = 'iterate',
|
|
98
|
-
p.createdAt = localDateTime()
|
|
99
|
-
MERGE (t:Task {id: $taskId})
|
|
100
|
-
ON CREATE SET
|
|
101
|
-
t.companyId = $cid,
|
|
102
|
-
t.title = 'Kaizen: ' + substring($problem, 0, 120),
|
|
103
|
-
t.body = $countermeasure,
|
|
104
|
-
t.originKind = 'kaizen_implementation',
|
|
105
|
-
t.assigneeAgentId = $owner,
|
|
106
|
-
t.status = 'todo',
|
|
107
|
-
t.priority = 2,
|
|
108
|
-
t.workType = 'improvement',
|
|
109
|
-
t.pdsaProcessed = false,
|
|
110
|
-
t.andoning = false,
|
|
111
|
-
t.createdAt = localDateTime()
|
|
112
|
-
MERGE (t)-[:IMPLEMENTS]->(p)
|
|
113
|
-
WITH p
|
|
114
|
-
MATCH (kp:KaizenProposal {id: $kpId})
|
|
115
|
-
MERGE (p)-[:PROMPTED_BY]->(kp)`,
|
|
116
|
-
{
|
|
117
|
-
cycleId,
|
|
118
|
-
taskId,
|
|
119
|
-
cid: this.companyId,
|
|
120
|
-
hypothesis: hypothesis ?? '',
|
|
121
|
-
countermeasure: countermeasure ?? '',
|
|
122
|
-
problem: problem ?? 'Kaizen improvement task',
|
|
123
|
-
owner,
|
|
124
|
-
kpId,
|
|
125
|
-
}
|
|
126
|
-
).catch(e => {
|
|
127
|
-
// log but don't throw — MERGE is idempotent, partial creation acceptable
|
|
128
|
-
console.warn(`[ApprovalExpiry] PDSACycle/Task creation partial for kpId=${kpId}: ${e.message}`);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
// Mark KaizenProposal accepted
|
|
132
|
-
await this.mg(
|
|
133
|
-
`MATCH (kp:KaizenProposal {id: $kpId}) SET kp.status = 'accepted'`,
|
|
134
|
-
{ kpId }
|
|
135
|
-
).catch(() => {});
|
|
136
|
-
|
|
137
|
-
// Transition original andon_paused task to help_pending — a responder is now
|
|
138
|
-
// working on the improvement. The original task remains paused but its status
|
|
139
|
-
// reflects that help is actively in progress rather than just requested.
|
|
140
|
-
await this.mg(
|
|
141
|
-
`MATCH (t:Task {companyId: $cid, andoning: true})
|
|
142
|
-
WHERE t.status = 'andon_paused'
|
|
143
|
-
SET t.status = 'help_pending',
|
|
144
|
-
t.helpPendingAt = localDateTime()`,
|
|
145
|
-
{ cid: this.companyId }
|
|
146
|
-
).catch(() => {});
|
|
147
|
-
|
|
148
|
-
// Emit AgentReadySignal so the new improvement task gets dispatched immediately
|
|
149
|
-
await this.mg(
|
|
150
|
-
`MERGE (s:AgentReadySignal {id: $arsId})
|
|
151
|
-
ON CREATE SET
|
|
152
|
-
s.agentId = $owner,
|
|
153
|
-
s.companyId = $cid,
|
|
154
|
-
s.status = 'pending',
|
|
155
|
-
s.createdAt = localDateTime(),
|
|
156
|
-
s.claimedBy = null`,
|
|
157
|
-
{ arsId: `ars:kaizen:${taskId}`, owner, cid: this.companyId }
|
|
158
|
-
).catch(() => {});
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
module.exports = { ApprovalExpiry };
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { execFile } = require('child_process');
|
|
4
|
-
const { promisify } = require('util');
|
|
5
|
-
const execFileAsync = promisify(execFile);
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* BlastRadiusAnalyzer — measures the impact of a HED operation's execution.
|
|
9
|
-
* Compares declared target vs actual changes (git diff + Memgraph diff).
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
class BlastRadiusAnalyzer {
|
|
13
|
-
constructor(mg, repoPath) {
|
|
14
|
-
this._mg = mg;
|
|
15
|
-
this._repoPath = repoPath || process.cwd();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async analyze(opId, operation, executionLockedAt, completedAt) {
|
|
19
|
-
const [filesChanged, nodesAffected] = await Promise.allSettled([
|
|
20
|
-
this._getChangedFiles(operation.target),
|
|
21
|
-
this._getChangedNodes(executionLockedAt, completedAt)
|
|
22
|
-
]);
|
|
23
|
-
|
|
24
|
-
const changedFiles = filesChanged.status === 'fulfilled' ? filesChanged.value : [];
|
|
25
|
-
const changedNodes = nodesAffected.status === 'fulfilled' ? nodesAffected.value : [];
|
|
26
|
-
|
|
27
|
-
const outsideTarget = changedFiles.filter(f => !f.includes(operation.target || ''));
|
|
28
|
-
const severity = this._computeSeverity(outsideTarget, changedNodes);
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
filesChanged: outsideTarget,
|
|
32
|
-
allFilesChanged: changedFiles,
|
|
33
|
-
nodesAffected: changedNodes,
|
|
34
|
-
severity,
|
|
35
|
-
opId
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async _getChangedFiles(declaredTarget) {
|
|
40
|
-
try {
|
|
41
|
-
const { stdout } = await execFileAsync('git', ['diff', '--name-only', 'HEAD~1'], {
|
|
42
|
-
cwd: this._repoPath,
|
|
43
|
-
windowsHide: true
|
|
44
|
-
});
|
|
45
|
-
return stdout.trim().split('\n').filter(Boolean);
|
|
46
|
-
} catch {
|
|
47
|
-
return [];
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async _getChangedNodes(executionLockedAt, completedAt) {
|
|
52
|
-
if (!executionLockedAt || !completedAt) return [];
|
|
53
|
-
try {
|
|
54
|
-
const rows = await this._mg(
|
|
55
|
-
`MATCH (n) WHERE n.updatedAt >= $from AND n.updatedAt <= $to
|
|
56
|
-
RETURN DISTINCT labels(n)[0] AS label, count(n) AS count LIMIT 20`,
|
|
57
|
-
{ from: executionLockedAt, to: completedAt }
|
|
58
|
-
);
|
|
59
|
-
return (rows || []).map(r => `${r.label}(${r.count})`);
|
|
60
|
-
} catch {
|
|
61
|
-
return [];
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
_computeSeverity(outsideFiles, changedNodes) {
|
|
66
|
-
const score = outsideFiles.length * 2 + changedNodes.length;
|
|
67
|
-
if (score === 0) return 'none';
|
|
68
|
-
if (score <= 2) return 'low';
|
|
69
|
-
if (score <= 6) return 'medium';
|
|
70
|
-
if (score <= 12) return 'high';
|
|
71
|
-
return 'critical';
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
module.exports = { BlastRadiusAnalyzer };
|