blun-king-cli 9.0.0 → 9.0.1
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/LIESMICH.txt +1 -7
- package/README.md +4 -16
- package/bin/blun.js +248 -160
- package/bin/core-bootstrap.js +47 -0
- package/bin/king.js +277 -1
- package/bin/launcher-mode.js +2 -1
- package/bin/launcher-runtime.js +221 -0
- package/bin/plugin-bootstrap.js +0 -0
- package/bin/private-paths.js +0 -0
- package/bin/update-lease.js +399 -0
- package/bin/update-notice.js +1094 -0
- package/blun.mjs +4060 -6667
- package/package.json +3 -10
- package/skills/screenshot-lesen/SKILL.md +0 -1
- package/skills/web-lesen/SKILL.md +0 -1
- package/telegram-plugin/dist/bridge.mjs +1 -21
- package/mnemo/access_routes.js +0 -692
- package/mnemo/agent_governance.js +0 -4242
- package/mnemo/agent_mail.js +0 -901
- package/mnemo/bootstrap_auto.js +0 -137
- package/mnemo/brief_coordination.js +0 -226
- package/mnemo/code_read_tools.js +0 -375
- package/mnemo/context_preview_tools.js +0 -603
- package/mnemo/embeddings.js +0 -66
- package/mnemo/external_repo_ops.js +0 -575
- package/mnemo/facts/example-project-rules.json +0 -90
- package/mnemo/facts/example.json +0 -34
- package/mnemo/identity_schema.sql +0 -139
- package/mnemo/journal_schema.js +0 -561
- package/mnemo/loop_doctor_tools.js +0 -661
- package/mnemo/mail_secret_refs.js +0 -150
- package/mnemo/mcp.js +0 -9309
- package/mnemo/memory_consolidation.js +0 -1914
- package/mnemo/memory_health_tools.js +0 -165
- package/mnemo/package.json +0 -79
- package/mnemo/protected_scope_gate.js +0 -627
- package/mnemo/resource_access_control.js +0 -684
- package/mnemo/runtime_governance.js +0 -1256
- package/mnemo/runtime_turn_gate.js +0 -862
- package/mnemo/sandbox.js +0 -143
- package/mnemo/schema.sql +0 -389
- package/mnemo/shared_utils.js +0 -763
- package/mnemo/skills/agent-auto-resume/SKILL.md +0 -56
- package/mnemo/skills/agent_hand/SKILL.md +0 -43
- package/mnemo/skills/agent_hand/run.js +0 -63
- package/mnemo/skills/book_flight/SKILL.md +0 -34
- package/mnemo/skills/external_repo_review/SKILL.md +0 -43
- package/mnemo/skills/external_repo_review/run.js +0 -73
- package/mnemo/skills/pay_invoice/SKILL.md +0 -34
- package/mnemo/team_quality_ops.js +0 -944
- package/mnemo/timeline_report_tools.js +0 -810
- package/mnemo/write_gate_risk.js +0 -80
- package/mnemo/writer_health.js +0 -152
- package/skills/doku-ingestion/SKILL.md +0 -48
- package/skills/doku-ingestion/ingest_docs.py +0 -133
|
@@ -1,4242 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const crypto = require("crypto");
|
|
4
|
-
const {
|
|
5
|
-
boolFlag,
|
|
6
|
-
cleanScope,
|
|
7
|
-
compactContent,
|
|
8
|
-
jsonSafe,
|
|
9
|
-
normalizeAgentName,
|
|
10
|
-
parseMaybeJson,
|
|
11
|
-
uniqueStrings,
|
|
12
|
-
} = require("./shared_utils");
|
|
13
|
-
const { normalizeResourceKind, normalizeResourceKey } = require("./resource_access_control");
|
|
14
|
-
|
|
15
|
-
const DEFAULT_SCOPE = "default";
|
|
16
|
-
const RISKY_ACTIONS = new Set(["code_edit", "write", "delete", "move", "deploy", "external_comm", "migration", "billing", "auth", "production"]);
|
|
17
|
-
|
|
18
|
-
const GSTACK_WORKFLOW = ["think", "plan", "build", "review", "test", "ship", "reflect", "memorize"];
|
|
19
|
-
|
|
20
|
-
const MNEMO_AGENT_OS_KERNEL = {
|
|
21
|
-
boot_order: [
|
|
22
|
-
"identify_agent_task_portal_risk",
|
|
23
|
-
"load_owner_rules",
|
|
24
|
-
"load_portal_rules",
|
|
25
|
-
"load_active_claims_and_locks",
|
|
26
|
-
"load_latest_receipts_and_handoffs",
|
|
27
|
-
"load_scars_and_forbidden_actions",
|
|
28
|
-
"select_gstack_role",
|
|
29
|
-
"create_or_attach_work_claim",
|
|
30
|
-
"produce_plan_before_build",
|
|
31
|
-
"write_receipts_during_work",
|
|
32
|
-
"write_handoff_before_stop"
|
|
33
|
-
],
|
|
34
|
-
laws: [
|
|
35
|
-
"owner_rules_override_everything",
|
|
36
|
-
"forbidden_actions_block_execution",
|
|
37
|
-
"protected_scopes_need_claim_and_evidence",
|
|
38
|
-
"done_is_invalid_without_evidence",
|
|
39
|
-
"stop_is_invalid_without_handoff",
|
|
40
|
-
"repeated_failure_creates_a_scar"
|
|
41
|
-
],
|
|
42
|
-
contract: [
|
|
43
|
-
"never_start_without_boot",
|
|
44
|
-
"never_edit_without_claim",
|
|
45
|
-
"never_mark_done_without_evidence",
|
|
46
|
-
"never_stop_without_handoff",
|
|
47
|
-
"never_ignore_owner_rules",
|
|
48
|
-
"never_overwrite_final_designs_or_protected_scopes",
|
|
49
|
-
"never_duplicate_work_already_claimed",
|
|
50
|
-
"never_hide_uncertainty_missing_tests_or_blockers",
|
|
51
|
-
"always_convert_important_telegram_or_brief_content_into_memory",
|
|
52
|
-
"always_create_scars_for_mistakes_that_must_not_repeat"
|
|
53
|
-
]
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const GSTACK_ROLE_CATALOG = [
|
|
57
|
-
{
|
|
58
|
-
role: "Product Planner",
|
|
59
|
-
department_name: "product",
|
|
60
|
-
aliases: ["YC Office Hours", "Product Interrogator", "CEO", "Founder Reviewer", "CEO / Founder Reviewer"],
|
|
61
|
-
mission: "Understand the user problem, define the narrowest valuable wedge, and keep scope honest before code starts.",
|
|
62
|
-
pre_work: ["problem understood", "existing work checked", "acceptance criteria written", "plan saved"],
|
|
63
|
-
during_work: ["decision changes captured", "scope drift flagged"],
|
|
64
|
-
post_work: ["outcome summarized", "remaining product risks marked"]
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
role: "System Architect",
|
|
68
|
-
department_name: "architecture",
|
|
69
|
-
aliases: ["Engineering Manager", "Architect", "Engineering Manager / Architect"],
|
|
70
|
-
mission: "Lock data flow, contracts, dependencies, failure modes, and cross-portal boundaries.",
|
|
71
|
-
pre_work: ["architecture context loaded", "dependencies checked", "protected scopes checked"],
|
|
72
|
-
during_work: ["architecture decisions captured", "risk map updated"],
|
|
73
|
-
post_work: ["architecture handoff written", "rollback path documented"]
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
role: "Backend Engineer",
|
|
77
|
-
department_name: "backend",
|
|
78
|
-
aliases: ["Backend", "API Engineer", "Data Engineer"],
|
|
79
|
-
mission: "Implement APIs, data models, workers, auth-adjacent backend logic, and reliable server behavior.",
|
|
80
|
-
pre_work: ["API rules checked", "data ownership checked", "work claim active"],
|
|
81
|
-
during_work: ["changed endpoints documented", "tests captured"],
|
|
82
|
-
post_work: ["server verification stored", "handoff written"]
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
role: "Frontend Engineer",
|
|
86
|
-
department_name: "frontend",
|
|
87
|
-
aliases: ["Senior Designer", "Designer", "UI Engineer", "Frontend", "Design Reviewer"],
|
|
88
|
-
mission: "Implement user-facing UI with responsive behavior, accessibility, and design-system alignment.",
|
|
89
|
-
pre_work: ["design rules loaded", "routes checked", "portal context loaded"],
|
|
90
|
-
during_work: ["screens and affected routes marked", "visual risks stored"],
|
|
91
|
-
post_work: ["browser evidence stored", "responsive checks captured"]
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
role: "Security Reviewer",
|
|
95
|
-
department_name: "security",
|
|
96
|
-
aliases: ["Chief Security Officer", "CSO", "Security"],
|
|
97
|
-
mission: "Review auth, data exposure, prompt injection, destructive actions, and protected scopes before release.",
|
|
98
|
-
pre_work: ["threat model context loaded", "protected systems checked"],
|
|
99
|
-
during_work: ["findings verified", "false positives excluded with reason"],
|
|
100
|
-
post_work: ["security verdict and evidence saved"]
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
role: "QA Tester",
|
|
104
|
-
department_name: "qa",
|
|
105
|
-
aliases: ["QA Lead", "QA", "Browser QA"],
|
|
106
|
-
mission: "Exercise real flows, capture regressions, verify fixes, and keep test evidence durable.",
|
|
107
|
-
pre_work: ["test matrix loaded", "known findings checked"],
|
|
108
|
-
during_work: ["each bug captured with reproduction", "fix verification stored"],
|
|
109
|
-
post_work: ["QA summary and remaining blockers written"]
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
role: "Release Manager",
|
|
113
|
-
department_name: "release",
|
|
114
|
-
aliases: ["Release Engineer", "Ship", "Deploy Manager"],
|
|
115
|
-
mission: "Coordinate ship, deploy, health checks, canaries, rollback, and final completion gate.",
|
|
116
|
-
pre_work: ["release rules checked", "evidence requirements checked"],
|
|
117
|
-
during_work: ["deploy receipts stored", "health status tracked"],
|
|
118
|
-
post_work: ["ship report and rollback status written"]
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
role: "Memory/Audit Officer",
|
|
122
|
-
department_name: "memory-audit",
|
|
123
|
-
aliases: ["Memory Officer", "Audit Officer", "Memorize", "Retro"],
|
|
124
|
-
mission: "Ensure every decision, change, blocker, evidence item, and handoff becomes structured Mnemo memory.",
|
|
125
|
-
pre_work: ["capture status checked", "recall and rules loaded"],
|
|
126
|
-
during_work: ["receipts enforced", "missing evidence blocked"],
|
|
127
|
-
post_work: ["handoff completeness verified"]
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
role: "Customer/Support Officer",
|
|
131
|
-
department_name: "support",
|
|
132
|
-
aliases: ["Support Officer", "Customer Officer", "Telegram/Brief Officer"],
|
|
133
|
-
mission: "Translate customer reports into tasks, status updates, acceptance criteria, and support-safe communication.",
|
|
134
|
-
pre_work: ["customer context loaded", "public/private channel rules checked"],
|
|
135
|
-
during_work: ["customer impact marked", "communication risks stored"],
|
|
136
|
-
post_work: ["support update prepared"]
|
|
137
|
-
},
|
|
138
|
-
{
|
|
139
|
-
role: "Portal Owner",
|
|
140
|
-
department_name: "portal-owner",
|
|
141
|
-
aliases: ["Portal", "Brand Owner", "Surface Owner"],
|
|
142
|
-
mission: "Own one portal's brand, domain, language, permissions, prices, design, and customer/partner rules.",
|
|
143
|
-
pre_work: ["portal context loaded", "global/portal/customer rules loaded"],
|
|
144
|
-
during_work: ["affected portal marked on every change"],
|
|
145
|
-
post_work: ["portal readiness and open risks updated"]
|
|
146
|
-
}
|
|
147
|
-
];
|
|
148
|
-
|
|
149
|
-
const GSTACK_CORE_SKILLS = [
|
|
150
|
-
["office-hours", "Product Planner", "think", "Reframe the request with forcing questions before implementation."],
|
|
151
|
-
["plan-ceo-review", "Product Planner", "plan", "Challenge scope and find the strongest product direction."],
|
|
152
|
-
["plan-eng-review", "System Architect", "plan", "Lock architecture, data flow, edge cases, tests, and failure modes."],
|
|
153
|
-
["plan-design-review", "Frontend Engineer", "plan", "Review visual quality and design requirements before build."],
|
|
154
|
-
["plan-devex-review", "Product Planner", "plan", "Review developer experience, personas, and time-to-hello-world."],
|
|
155
|
-
["autoplan", "System Architect", "plan", "Run product, design, engineering, and DX plan review as one gate."],
|
|
156
|
-
["design-consultation", "Frontend Engineer", "plan", "Create a full design system and creative direction."],
|
|
157
|
-
["review", "System Architect", "review", "Find bugs, completeness gaps, and production risks before landing."],
|
|
158
|
-
["investigate", "Backend Engineer", "review", "Root-cause debugging; no fixes before investigation."],
|
|
159
|
-
["design-review", "Frontend Engineer", "review", "Live visual audit and fix loop with browser evidence."],
|
|
160
|
-
["devex-review", "QA Tester", "review", "Live developer-experience audit against real onboarding/docs."],
|
|
161
|
-
["design-shotgun", "Frontend Engineer", "build", "Generate and compare multiple design directions before implementation."],
|
|
162
|
-
["design-html", "Frontend Engineer", "build", "Turn approved design into production-quality HTML/CSS or framework code."],
|
|
163
|
-
["qa", "QA Tester", "test", "Open a real browser, find bugs, fix, re-test, and store regression evidence."],
|
|
164
|
-
["qa-only", "QA Tester", "test", "Report-only QA with reproductions and evidence."],
|
|
165
|
-
["cso", "Security Reviewer", "review", "OWASP/STRIDE security audit with verified findings."],
|
|
166
|
-
["ship", "Release Manager", "ship", "Run tests, docs checks, push/open PR, and verify readiness."],
|
|
167
|
-
["land-and-deploy", "Release Manager", "ship", "Merge, deploy, wait for CI, and verify production health."],
|
|
168
|
-
["canary", "Release Manager", "ship", "Post-deploy monitoring loop and regression watch."],
|
|
169
|
-
["benchmark", "QA Tester", "test", "Performance and Core Web Vitals regression baseline."],
|
|
170
|
-
["document-release", "Memory/Audit Officer", "reflect", "Update docs to match shipped changes."],
|
|
171
|
-
["document-generate", "Memory/Audit Officer", "reflect", "Generate missing docs from code and decisions."],
|
|
172
|
-
["retro", "Memory/Audit Officer", "reflect", "Team-aware retro, shipping streaks, test health, and lessons learned."]
|
|
173
|
-
].map(([skill, role, phase, purpose]) => ({ skill, role, phase, purpose }));
|
|
174
|
-
|
|
175
|
-
const GSTACK_POWER_TOOLS = [
|
|
176
|
-
["reviewer", "cross-model second opinion and adversarial review"],
|
|
177
|
-
["careful", "destructive-command warning"],
|
|
178
|
-
["freeze", "directory edit lock"],
|
|
179
|
-
["guard", "careful plus freeze full safety"],
|
|
180
|
-
["unfreeze", "remove edit lock"],
|
|
181
|
-
["context-save", "save work state, decisions, and remaining work"],
|
|
182
|
-
["context-restore", "restore work state across sessions"],
|
|
183
|
-
["learn", "manage persistent learnings"],
|
|
184
|
-
["health", "code quality dashboard"],
|
|
185
|
-
["browse", "real Chromium browser actions"],
|
|
186
|
-
["open-gstack-browser", "visible browser with sidebar"],
|
|
187
|
-
["setup-browser-cookies", "authenticated browser testing"],
|
|
188
|
-
["pair-agent", "cross-agent shared browser with attribution"],
|
|
189
|
-
["setup-deploy", "deploy configuration detection"],
|
|
190
|
-
["setup-gbrain", "persistent brain onboarding pattern"],
|
|
191
|
-
["sync-gbrain", "keep code and memory index current"],
|
|
192
|
-
["gstack-upgrade", "self-updater pattern"],
|
|
193
|
-
["benchmark-models", "cross-model benchmark"]
|
|
194
|
-
].map(([tool, purpose]) => ({ tool, purpose }));
|
|
195
|
-
|
|
196
|
-
const MNEMO_OS_COMMANDS = [
|
|
197
|
-
["mnemo boot", "Load memory, claims, rules, scars, portal context, role, recent receipts, and board before work."],
|
|
198
|
-
["mnemo claim", "Reserve scope before touching files, routes, APIs, deployments, prompts, or portal logic."],
|
|
199
|
-
["mnemo receipt", "Append durable evidence for a meaningful action."],
|
|
200
|
-
["mnemo handoff", "Close or pause work with completed/changed/failed/open/tested/rollback state."],
|
|
201
|
-
["mnemo scar", "Create never-again prevention after a mistake, incident, or owner correction."],
|
|
202
|
-
["mnemo mission", "Generate current company, portal, agent, claims, and open-work dashboard."],
|
|
203
|
-
["/mnemo-office-hours", "Product interrogation and founder-style problem framing."],
|
|
204
|
-
["/mnemo-autoplan", "Product, design, engineering, and DX plan review before implementation."],
|
|
205
|
-
["/mnemo-eng-review", "Architecture, data-flow, edge-case, and failure-mode review."],
|
|
206
|
-
["/mnemo-design-review", "Visual and UX review with browser evidence where relevant."],
|
|
207
|
-
["/mnemo-review", "Bug, completeness, and production-risk review."],
|
|
208
|
-
["/mnemo-investigate", "Root-cause investigation before any fix."],
|
|
209
|
-
["/mnemo-qa", "Real-flow QA with reproduction, evidence, fix verification, and regression notes."],
|
|
210
|
-
["/mnemo-ship", "Tests, docs, deploy/canary, health checks, and rollback evidence."],
|
|
211
|
-
["/mnemo-retro", "Reflect, memorize, and update prevention after work completes."],
|
|
212
|
-
["/mnemo-cso", "Security and protected-scope review."],
|
|
213
|
-
["/mnemo-freeze", "Protect scope against parallel edits."],
|
|
214
|
-
["/mnemo-learn", "Store durable owner, project, or operational learning."]
|
|
215
|
-
].map(([command, purpose]) => ({ command, purpose }));
|
|
216
|
-
|
|
217
|
-
function nowIso() {
|
|
218
|
-
return new Date().toISOString();
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function sha(value) {
|
|
222
|
-
return crypto.createHash("sha256").update(String(value)).digest("hex");
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function scopeName(scope) {
|
|
226
|
-
return cleanScope(scope || DEFAULT_SCOPE);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function clampInt(value, fallback, min, max) {
|
|
230
|
-
const n = parseInt(value, 10);
|
|
231
|
-
if (!Number.isFinite(n)) return fallback;
|
|
232
|
-
return Math.min(Math.max(n, min), max);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function tableExists(db, tableName) {
|
|
236
|
-
try {
|
|
237
|
-
return !!db.prepare("SELECT 1 FROM sqlite_master WHERE type IN ('table','view') AND name=?").get(tableName);
|
|
238
|
-
} catch {
|
|
239
|
-
return false;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function safeJson(value, fallback) {
|
|
244
|
-
if (value === undefined) return JSON.stringify(fallback);
|
|
245
|
-
return jsonSafe(value, 30000) || JSON.stringify(fallback);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function parseJson(value, fallback) {
|
|
249
|
-
return parseMaybeJson(value, fallback);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function normalizeDepartment(value) {
|
|
253
|
-
return String(value || "")
|
|
254
|
-
.trim()
|
|
255
|
-
.toLowerCase()
|
|
256
|
-
.replace(/[^a-z0-9._-]+/g, "-")
|
|
257
|
-
.replace(/^-+|-+$/g, "") || "general";
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function normalizeRisk(value) {
|
|
261
|
-
return String(value || "normal")
|
|
262
|
-
.trim()
|
|
263
|
-
.toLowerCase()
|
|
264
|
-
.replace(/[^a-z0-9._-]+/g, "-")
|
|
265
|
-
.replace(/^-+|-+$/g, "") || "normal";
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function listInput(value) {
|
|
269
|
-
if (Array.isArray(value)) return uniqueStrings(value);
|
|
270
|
-
const parsed = parseMaybeJson(value, null);
|
|
271
|
-
if (Array.isArray(parsed)) return uniqueStrings(parsed);
|
|
272
|
-
return uniqueStrings(String(value || "").split(/[\n,;]+/).map((item) => item.trim()).filter(Boolean));
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function textOrNull(value, max = 8000) {
|
|
276
|
-
const text = compactContent(value, max);
|
|
277
|
-
return text && text.trim() ? text : null;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function roleKey(value) {
|
|
281
|
-
return String(value || "")
|
|
282
|
-
.trim()
|
|
283
|
-
.toLowerCase()
|
|
284
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
285
|
-
.replace(/^-+|-+$/g, "");
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
function canonicalRoleName(value) {
|
|
289
|
-
const key = roleKey(value);
|
|
290
|
-
const found = GSTACK_ROLE_CATALOG.find((role) => (
|
|
291
|
-
roleKey(role.role) === key ||
|
|
292
|
-
roleKey(role.department_name) === key ||
|
|
293
|
-
(Array.isArray(role.aliases) && role.aliases.some((alias) => roleKey(alias) === key))
|
|
294
|
-
));
|
|
295
|
-
return found ? found.role : null;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
function gstackRole(value) {
|
|
299
|
-
const name = canonicalRoleName(value);
|
|
300
|
-
return name ? GSTACK_ROLE_CATALOG.find((role) => role.role === name) : null;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function jsonList(value) {
|
|
304
|
-
if (Array.isArray(value)) return value.filter((item) => item !== undefined && item !== null && String(item).trim() !== "");
|
|
305
|
-
const parsed = parseMaybeJson(value, null);
|
|
306
|
-
if (Array.isArray(parsed)) return parsed;
|
|
307
|
-
if (value && typeof value === "object") return [value];
|
|
308
|
-
return String(value || "").split(/[\n,;]+/).map((item) => item.trim()).filter(Boolean);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
function hasValue(value) {
|
|
312
|
-
if (Array.isArray(value)) return value.length > 0;
|
|
313
|
-
if (value && typeof value === "object") return Object.keys(value).length > 0;
|
|
314
|
-
return String(value || "").trim().length > 0;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function portalName(input = {}) {
|
|
318
|
-
return textOrNull(input.portal || input.portal_name || input.surface || input.domain || "default", 200) || "default";
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function rowToRoleAssignment(row) {
|
|
322
|
-
if (!row) return null;
|
|
323
|
-
return {
|
|
324
|
-
id: row.id,
|
|
325
|
-
scope: row.scope,
|
|
326
|
-
project: row.project || null,
|
|
327
|
-
portal: row.portal || null,
|
|
328
|
-
agent_name: row.agent_name,
|
|
329
|
-
role_name: row.role_name,
|
|
330
|
-
department_name: row.department_name || null,
|
|
331
|
-
task: row.task || null,
|
|
332
|
-
plan_summary: row.plan_summary || null,
|
|
333
|
-
status: row.status,
|
|
334
|
-
selected_by: row.selected_by || null,
|
|
335
|
-
selected_at: row.selected_at,
|
|
336
|
-
updated_at: row.updated_at,
|
|
337
|
-
meta: parseJson(row.meta_json, {})
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
function rowToPortalContext(row) {
|
|
342
|
-
if (!row) return null;
|
|
343
|
-
return {
|
|
344
|
-
id: row.id,
|
|
345
|
-
scope: row.scope,
|
|
346
|
-
project: row.project,
|
|
347
|
-
portal: row.portal,
|
|
348
|
-
portal_id: row.portal_id || row.portal || null,
|
|
349
|
-
portal_name: row.portal_name || row.portal || null,
|
|
350
|
-
company_name: row.company_name || null,
|
|
351
|
-
brand_name: row.brand_name || null,
|
|
352
|
-
domain: row.domain || null,
|
|
353
|
-
environment: row.environment || null,
|
|
354
|
-
country_or_market: parseJson(row.country_or_market_json, []),
|
|
355
|
-
user_role: row.user_role || null,
|
|
356
|
-
language: row.language || null,
|
|
357
|
-
supported_languages: parseJson(row.supported_languages_json, []),
|
|
358
|
-
design: parseJson(row.design_json, null),
|
|
359
|
-
credit_system: parseJson(row.credit_system_json, null),
|
|
360
|
-
pricing: parseJson(row.pricing_json, null),
|
|
361
|
-
rights: parseJson(row.rights_json, null),
|
|
362
|
-
billing_owner: row.billing_owner || null,
|
|
363
|
-
auth_owner: row.auth_owner || null,
|
|
364
|
-
deployment_owner: row.deployment_owner || null,
|
|
365
|
-
legal_owner: row.legal_owner || null,
|
|
366
|
-
forbidden_cross_portal_leaks: parseJson(row.forbidden_cross_portal_leaks_json, []),
|
|
367
|
-
shared_modules: parseJson(row.shared_modules_json, []),
|
|
368
|
-
protected_surfaces: parseJson(row.protected_surfaces_json, []),
|
|
369
|
-
global_rules: parseJson(row.global_rules_json, []),
|
|
370
|
-
portal_rules: parseJson(row.portal_rules_json, []),
|
|
371
|
-
customer_partner_rules: parseJson(row.customer_partner_rules_json, []),
|
|
372
|
-
dependencies: parseJson(row.dependencies_json, []),
|
|
373
|
-
status: row.status,
|
|
374
|
-
updated_by: row.updated_by || null,
|
|
375
|
-
updated_at: row.updated_at,
|
|
376
|
-
meta: parseJson(row.meta_json, {})
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
function latestRoleAssignment(db, input = {}) {
|
|
381
|
-
ensureAgentGovernanceSchema(db);
|
|
382
|
-
const scope = scopeName(input.scope);
|
|
383
|
-
const agent = normalizeAgentName(input.agent_name);
|
|
384
|
-
const project = textOrNull(input.project, 500);
|
|
385
|
-
const portal = input.portal ? portalName(input) : null;
|
|
386
|
-
const where = ["scope=?", "agent_name=?", "status='active'"];
|
|
387
|
-
const params = [scope, agent];
|
|
388
|
-
if (project) { where.push("(project=? OR project IS NULL OR project='')"); params.push(project); }
|
|
389
|
-
if (portal) { where.push("(portal=? OR portal IS NULL OR portal='')"); params.push(portal); }
|
|
390
|
-
const row = db.prepare("SELECT * FROM agent_role_assignment WHERE " + where.join(" AND ") + " ORDER BY CASE WHEN project=? THEN 0 ELSE 1 END, CASE WHEN portal=? THEN 0 ELSE 1 END, updated_at DESC, id DESC LIMIT 1")
|
|
391
|
-
.get(...params, project || "", portal || "");
|
|
392
|
-
return rowToRoleAssignment(row);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
function claimScopeKey(kind, value) {
|
|
396
|
-
const normalizedKind = String(kind || "file").trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-") || "file";
|
|
397
|
-
const raw = String(value || "").trim();
|
|
398
|
-
if (!raw) return null;
|
|
399
|
-
const base = normalizedKind === "file" ? raw.replace(/\\/g, "/").toLowerCase() : raw.toLowerCase().replace(/[^a-z0-9._:/-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
400
|
-
return `${normalizedKind}:${base}`;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
function ensureMinimalEventJournalSchema(db) {
|
|
404
|
-
db.exec(`
|
|
405
|
-
CREATE TABLE IF NOT EXISTS mnemo_event_journal (
|
|
406
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
407
|
-
source TEXT NOT NULL,
|
|
408
|
-
channel TEXT,
|
|
409
|
-
direction TEXT NOT NULL DEFAULT 'internal',
|
|
410
|
-
actor TEXT,
|
|
411
|
-
actor_id TEXT,
|
|
412
|
-
event_kind TEXT NOT NULL,
|
|
413
|
-
ref_kind TEXT,
|
|
414
|
-
ref_id TEXT,
|
|
415
|
-
thread_id TEXT,
|
|
416
|
-
status TEXT,
|
|
417
|
-
content TEXT,
|
|
418
|
-
payload_json TEXT,
|
|
419
|
-
meta_json TEXT,
|
|
420
|
-
occurred_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
421
|
-
ingested_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
422
|
-
);
|
|
423
|
-
CREATE INDEX IF NOT EXISTS idx_event_journal_occurred ON mnemo_event_journal(occurred_at DESC);
|
|
424
|
-
CREATE INDEX IF NOT EXISTS idx_event_journal_source_channel ON mnemo_event_journal(source, channel, occurred_at DESC);
|
|
425
|
-
CREATE INDEX IF NOT EXISTS idx_event_journal_actor ON mnemo_event_journal(actor, occurred_at DESC);
|
|
426
|
-
CREATE INDEX IF NOT EXISTS idx_event_journal_kind ON mnemo_event_journal(event_kind, occurred_at DESC);
|
|
427
|
-
CREATE INDEX IF NOT EXISTS idx_event_journal_ref ON mnemo_event_journal(ref_kind, ref_id);
|
|
428
|
-
`);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
function ensureAgentGovernanceSchema(db) {
|
|
432
|
-
ensureMinimalEventJournalSchema(db);
|
|
433
|
-
db.exec(`
|
|
434
|
-
CREATE TABLE IF NOT EXISTS department_charter (
|
|
435
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
436
|
-
department_name TEXT NOT NULL,
|
|
437
|
-
mission TEXT,
|
|
438
|
-
responsibilities_json TEXT,
|
|
439
|
-
boundaries_json TEXT,
|
|
440
|
-
standard_permissions_json TEXT,
|
|
441
|
-
allowed_resources_json TEXT,
|
|
442
|
-
escalation_rules_json TEXT,
|
|
443
|
-
standing_permissions_json TEXT,
|
|
444
|
-
autonomy_floor INTEGER NOT NULL DEFAULT 0,
|
|
445
|
-
autonomy_ceiling INTEGER NOT NULL DEFAULT 3,
|
|
446
|
-
default_risk_class TEXT NOT NULL DEFAULT 'normal',
|
|
447
|
-
lead_agent TEXT,
|
|
448
|
-
review_agent TEXT,
|
|
449
|
-
status TEXT NOT NULL DEFAULT 'active',
|
|
450
|
-
meta_json TEXT,
|
|
451
|
-
updated_by TEXT,
|
|
452
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
453
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
454
|
-
PRIMARY KEY(scope, department_name)
|
|
455
|
-
);
|
|
456
|
-
CREATE INDEX IF NOT EXISTS idx_department_charter_status ON department_charter(status, updated_at DESC);
|
|
457
|
-
|
|
458
|
-
CREATE TABLE IF NOT EXISTS work_order_template (
|
|
459
|
-
template_id TEXT PRIMARY KEY,
|
|
460
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
461
|
-
title TEXT NOT NULL,
|
|
462
|
-
description TEXT,
|
|
463
|
-
department_name TEXT,
|
|
464
|
-
risk_class TEXT NOT NULL DEFAULT 'normal',
|
|
465
|
-
action_type TEXT,
|
|
466
|
-
allowed_tools_json TEXT,
|
|
467
|
-
allowed_resources_json TEXT,
|
|
468
|
-
done_criteria_json TEXT,
|
|
469
|
-
required_evidence_json TEXT,
|
|
470
|
-
quality_gates_json TEXT,
|
|
471
|
-
runtime_contract_json TEXT,
|
|
472
|
-
status TEXT NOT NULL DEFAULT 'active',
|
|
473
|
-
source TEXT NOT NULL DEFAULT 'custom',
|
|
474
|
-
meta_json TEXT,
|
|
475
|
-
updated_by TEXT,
|
|
476
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
477
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
478
|
-
);
|
|
479
|
-
CREATE INDEX IF NOT EXISTS idx_work_order_template_scope ON work_order_template(scope, status, template_id);
|
|
480
|
-
|
|
481
|
-
CREATE TABLE IF NOT EXISTS work_order (
|
|
482
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
483
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
484
|
-
project TEXT,
|
|
485
|
-
title TEXT NOT NULL,
|
|
486
|
-
objective TEXT NOT NULL,
|
|
487
|
-
department_name TEXT,
|
|
488
|
-
owner_agent TEXT,
|
|
489
|
-
assigned_agent TEXT,
|
|
490
|
-
status TEXT NOT NULL DEFAULT 'open',
|
|
491
|
-
risk_class TEXT NOT NULL DEFAULT 'normal',
|
|
492
|
-
action_type TEXT,
|
|
493
|
-
allowed_tools_json TEXT,
|
|
494
|
-
allowed_resources_json TEXT,
|
|
495
|
-
done_criteria_json TEXT,
|
|
496
|
-
required_evidence_json TEXT,
|
|
497
|
-
approval_ids_json TEXT,
|
|
498
|
-
deadline_at TEXT,
|
|
499
|
-
token_id TEXT,
|
|
500
|
-
source_ref TEXT,
|
|
501
|
-
created_by TEXT,
|
|
502
|
-
completion_summary TEXT,
|
|
503
|
-
handoff_id INTEGER,
|
|
504
|
-
evidence_json TEXT,
|
|
505
|
-
meta_json TEXT,
|
|
506
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
507
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
508
|
-
completed_at TEXT
|
|
509
|
-
);
|
|
510
|
-
CREATE INDEX IF NOT EXISTS idx_work_order_assigned ON work_order(assigned_agent, status, updated_at DESC);
|
|
511
|
-
CREATE INDEX IF NOT EXISTS idx_work_order_project ON work_order(project, status, updated_at DESC);
|
|
512
|
-
CREATE INDEX IF NOT EXISTS idx_work_order_department ON work_order(department_name, status, updated_at DESC);
|
|
513
|
-
|
|
514
|
-
CREATE TABLE IF NOT EXISTS capability_token (
|
|
515
|
-
token_id TEXT PRIMARY KEY,
|
|
516
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
517
|
-
work_order_id INTEGER,
|
|
518
|
-
agent_name TEXT NOT NULL,
|
|
519
|
-
department_name TEXT,
|
|
520
|
-
project TEXT,
|
|
521
|
-
risk_class TEXT NOT NULL DEFAULT 'normal',
|
|
522
|
-
action_type TEXT,
|
|
523
|
-
allowed_tools_json TEXT,
|
|
524
|
-
allowed_resources_json TEXT,
|
|
525
|
-
required_evidence_json TEXT,
|
|
526
|
-
approval_ids_json TEXT,
|
|
527
|
-
budgets_json TEXT,
|
|
528
|
-
status TEXT NOT NULL DEFAULT 'active',
|
|
529
|
-
granted_by TEXT,
|
|
530
|
-
reason TEXT,
|
|
531
|
-
issued_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
532
|
-
expires_at TEXT NOT NULL,
|
|
533
|
-
revoked_at TEXT,
|
|
534
|
-
meta_json TEXT
|
|
535
|
-
);
|
|
536
|
-
CREATE INDEX IF NOT EXISTS idx_capability_token_agent ON capability_token(agent_name, status, expires_at);
|
|
537
|
-
CREATE INDEX IF NOT EXISTS idx_capability_token_work_order ON capability_token(work_order_id, status);
|
|
538
|
-
|
|
539
|
-
CREATE TABLE IF NOT EXISTS capability_token_audit (
|
|
540
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
541
|
-
token_id TEXT,
|
|
542
|
-
work_order_id INTEGER,
|
|
543
|
-
agent_name TEXT,
|
|
544
|
-
project TEXT,
|
|
545
|
-
event_kind TEXT NOT NULL,
|
|
546
|
-
granted INTEGER NOT NULL DEFAULT 0,
|
|
547
|
-
reason TEXT,
|
|
548
|
-
matched_scope_json TEXT,
|
|
549
|
-
missing_approval INTEGER NOT NULL DEFAULT 0,
|
|
550
|
-
required_evidence_json TEXT,
|
|
551
|
-
action_payload_json TEXT,
|
|
552
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
553
|
-
);
|
|
554
|
-
CREATE INDEX IF NOT EXISTS idx_capability_audit_token ON capability_token_audit(token_id, created_at DESC);
|
|
555
|
-
CREATE INDEX IF NOT EXISTS idx_capability_audit_agent ON capability_token_audit(agent_name, created_at DESC);
|
|
556
|
-
|
|
557
|
-
CREATE TABLE IF NOT EXISTS intent_route (
|
|
558
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
559
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
560
|
-
intent_kind TEXT NOT NULL,
|
|
561
|
-
agent_name TEXT,
|
|
562
|
-
project TEXT,
|
|
563
|
-
department_name TEXT,
|
|
564
|
-
resource_kind TEXT,
|
|
565
|
-
resource_key TEXT,
|
|
566
|
-
summary TEXT,
|
|
567
|
-
route_to_agent TEXT,
|
|
568
|
-
route_to_department TEXT,
|
|
569
|
-
reason TEXT,
|
|
570
|
-
status TEXT NOT NULL DEFAULT 'routed',
|
|
571
|
-
brief_id INTEGER,
|
|
572
|
-
meta_json TEXT,
|
|
573
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
574
|
-
);
|
|
575
|
-
CREATE INDEX IF NOT EXISTS idx_intent_route_agent ON intent_route(route_to_agent, status, created_at DESC);
|
|
576
|
-
CREATE INDEX IF NOT EXISTS idx_intent_route_project ON intent_route(project, status, created_at DESC);
|
|
577
|
-
|
|
578
|
-
CREATE TABLE IF NOT EXISTS context_snapshot (
|
|
579
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
580
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
581
|
-
project TEXT,
|
|
582
|
-
agent_name TEXT,
|
|
583
|
-
runtime_name TEXT,
|
|
584
|
-
work_order_id INTEGER,
|
|
585
|
-
title TEXT,
|
|
586
|
-
summary TEXT,
|
|
587
|
-
decisions_json TEXT,
|
|
588
|
-
remaining_work_json TEXT,
|
|
589
|
-
files_json TEXT,
|
|
590
|
-
routes_json TEXT,
|
|
591
|
-
urls_json TEXT,
|
|
592
|
-
branch TEXT,
|
|
593
|
-
commit_sha TEXT,
|
|
594
|
-
dirty INTEGER NOT NULL DEFAULT 0,
|
|
595
|
-
source_ref TEXT,
|
|
596
|
-
status TEXT NOT NULL DEFAULT 'active',
|
|
597
|
-
meta_json TEXT,
|
|
598
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
599
|
-
);
|
|
600
|
-
CREATE INDEX IF NOT EXISTS idx_context_snapshot_project ON context_snapshot(project, created_at DESC);
|
|
601
|
-
CREATE INDEX IF NOT EXISTS idx_context_snapshot_agent ON context_snapshot(agent_name, created_at DESC);
|
|
602
|
-
CREATE INDEX IF NOT EXISTS idx_context_snapshot_work_order ON context_snapshot(work_order_id, created_at DESC);
|
|
603
|
-
|
|
604
|
-
CREATE TABLE IF NOT EXISTS agent_role_assignment (
|
|
605
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
606
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
607
|
-
project TEXT,
|
|
608
|
-
portal TEXT,
|
|
609
|
-
agent_name TEXT NOT NULL,
|
|
610
|
-
role_name TEXT NOT NULL,
|
|
611
|
-
department_name TEXT,
|
|
612
|
-
task TEXT,
|
|
613
|
-
plan_summary TEXT,
|
|
614
|
-
status TEXT NOT NULL DEFAULT 'active',
|
|
615
|
-
selected_by TEXT,
|
|
616
|
-
meta_json TEXT,
|
|
617
|
-
selected_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
618
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
619
|
-
);
|
|
620
|
-
CREATE INDEX IF NOT EXISTS idx_agent_role_assignment_agent ON agent_role_assignment(scope, agent_name, status, updated_at DESC);
|
|
621
|
-
CREATE INDEX IF NOT EXISTS idx_agent_role_assignment_project ON agent_role_assignment(scope, project, role_name, status, updated_at DESC);
|
|
622
|
-
|
|
623
|
-
CREATE TABLE IF NOT EXISTS portal_context (
|
|
624
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
625
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
626
|
-
project TEXT NOT NULL,
|
|
627
|
-
portal TEXT NOT NULL DEFAULT 'default',
|
|
628
|
-
portal_id TEXT,
|
|
629
|
-
portal_name TEXT,
|
|
630
|
-
company_name TEXT,
|
|
631
|
-
brand_name TEXT,
|
|
632
|
-
domain TEXT,
|
|
633
|
-
environment TEXT,
|
|
634
|
-
country_or_market_json TEXT,
|
|
635
|
-
user_role TEXT,
|
|
636
|
-
language TEXT,
|
|
637
|
-
supported_languages_json TEXT,
|
|
638
|
-
design_json TEXT,
|
|
639
|
-
credit_system_json TEXT,
|
|
640
|
-
pricing_json TEXT,
|
|
641
|
-
rights_json TEXT,
|
|
642
|
-
billing_owner TEXT,
|
|
643
|
-
auth_owner TEXT,
|
|
644
|
-
deployment_owner TEXT,
|
|
645
|
-
legal_owner TEXT,
|
|
646
|
-
forbidden_cross_portal_leaks_json TEXT,
|
|
647
|
-
shared_modules_json TEXT,
|
|
648
|
-
protected_surfaces_json TEXT,
|
|
649
|
-
global_rules_json TEXT,
|
|
650
|
-
portal_rules_json TEXT,
|
|
651
|
-
customer_partner_rules_json TEXT,
|
|
652
|
-
dependencies_json TEXT,
|
|
653
|
-
status TEXT NOT NULL DEFAULT 'active',
|
|
654
|
-
updated_by TEXT,
|
|
655
|
-
meta_json TEXT,
|
|
656
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
657
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
658
|
-
UNIQUE(scope, project, portal)
|
|
659
|
-
);
|
|
660
|
-
CREATE INDEX IF NOT EXISTS idx_portal_context_project ON portal_context(scope, project, status, updated_at DESC);
|
|
661
|
-
|
|
662
|
-
CREATE TABLE IF NOT EXISTS agent_workflow_receipt (
|
|
663
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
664
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
665
|
-
project TEXT,
|
|
666
|
-
portal TEXT,
|
|
667
|
-
agent_name TEXT NOT NULL,
|
|
668
|
-
role_name TEXT,
|
|
669
|
-
phase TEXT NOT NULL,
|
|
670
|
-
summary TEXT NOT NULL,
|
|
671
|
-
evidence_json TEXT,
|
|
672
|
-
risks_json TEXT,
|
|
673
|
-
affected_portals_json TEXT,
|
|
674
|
-
status TEXT NOT NULL DEFAULT 'done',
|
|
675
|
-
meta_json TEXT,
|
|
676
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
677
|
-
);
|
|
678
|
-
CREATE INDEX IF NOT EXISTS idx_agent_workflow_receipt_agent ON agent_workflow_receipt(scope, agent_name, project, created_at DESC);
|
|
679
|
-
CREATE INDEX IF NOT EXISTS idx_agent_workflow_receipt_project ON agent_workflow_receipt(scope, project, phase, created_at DESC);
|
|
680
|
-
|
|
681
|
-
CREATE TABLE IF NOT EXISTS rule_violation_log (
|
|
682
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
683
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
684
|
-
project TEXT,
|
|
685
|
-
portal TEXT,
|
|
686
|
-
agent_name TEXT NOT NULL,
|
|
687
|
-
rule_key TEXT NOT NULL,
|
|
688
|
-
rule_text TEXT,
|
|
689
|
-
action_kind TEXT,
|
|
690
|
-
target TEXT,
|
|
691
|
-
severity TEXT NOT NULL DEFAULT 'M',
|
|
692
|
-
status TEXT NOT NULL DEFAULT 'open',
|
|
693
|
-
evidence_json TEXT,
|
|
694
|
-
prevention_json TEXT,
|
|
695
|
-
meta_json TEXT,
|
|
696
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
697
|
-
resolved_at TEXT
|
|
698
|
-
);
|
|
699
|
-
CREATE INDEX IF NOT EXISTS idx_rule_violation_project ON rule_violation_log(scope, project, status, created_at DESC);
|
|
700
|
-
CREATE INDEX IF NOT EXISTS idx_rule_violation_agent ON rule_violation_log(agent_name, status, created_at DESC);
|
|
701
|
-
CREATE INDEX IF NOT EXISTS idx_rule_violation_rule ON rule_violation_log(rule_key, status, created_at DESC);
|
|
702
|
-
|
|
703
|
-
CREATE TABLE IF NOT EXISTS owner_rule_snapshot (
|
|
704
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
705
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
706
|
-
project TEXT NOT NULL,
|
|
707
|
-
rules_hash TEXT NOT NULL,
|
|
708
|
-
rules_json TEXT NOT NULL,
|
|
709
|
-
created_by TEXT,
|
|
710
|
-
meta_json TEXT,
|
|
711
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
712
|
-
);
|
|
713
|
-
CREATE INDEX IF NOT EXISTS idx_owner_rule_snapshot_project ON owner_rule_snapshot(scope, project, created_at DESC);
|
|
714
|
-
|
|
715
|
-
CREATE TABLE IF NOT EXISTS task_fingerprint (
|
|
716
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
717
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
718
|
-
project TEXT,
|
|
719
|
-
portal TEXT,
|
|
720
|
-
fingerprint TEXT NOT NULL,
|
|
721
|
-
summary TEXT NOT NULL,
|
|
722
|
-
normalized_text TEXT NOT NULL,
|
|
723
|
-
agent_name TEXT,
|
|
724
|
-
source_kind TEXT,
|
|
725
|
-
source_id TEXT,
|
|
726
|
-
status TEXT NOT NULL DEFAULT 'active',
|
|
727
|
-
linked_task_id INTEGER,
|
|
728
|
-
meta_json TEXT,
|
|
729
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
730
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
731
|
-
);
|
|
732
|
-
CREATE INDEX IF NOT EXISTS idx_task_fingerprint_project ON task_fingerprint(scope, project, status, updated_at DESC);
|
|
733
|
-
CREATE INDEX IF NOT EXISTS idx_task_fingerprint_hash ON task_fingerprint(scope, fingerprint, status);
|
|
734
|
-
|
|
735
|
-
CREATE TABLE IF NOT EXISTS quality_gate_run (
|
|
736
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
737
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
738
|
-
gate_id TEXT NOT NULL,
|
|
739
|
-
project TEXT,
|
|
740
|
-
work_order_id INTEGER,
|
|
741
|
-
agent_name TEXT,
|
|
742
|
-
status TEXT NOT NULL,
|
|
743
|
-
score INTEGER NOT NULL DEFAULT 0,
|
|
744
|
-
missing_json TEXT,
|
|
745
|
-
invalid_json TEXT,
|
|
746
|
-
evidence_json TEXT,
|
|
747
|
-
meta_json TEXT,
|
|
748
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
749
|
-
);
|
|
750
|
-
CREATE INDEX IF NOT EXISTS idx_quality_gate_run_project ON quality_gate_run(project, gate_id, created_at DESC);
|
|
751
|
-
CREATE INDEX IF NOT EXISTS idx_quality_gate_run_work_order ON quality_gate_run(work_order_id, created_at DESC);
|
|
752
|
-
|
|
753
|
-
CREATE TABLE IF NOT EXISTS autonomy_score_snapshot (
|
|
754
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
755
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
756
|
-
agent_name TEXT NOT NULL,
|
|
757
|
-
score INTEGER NOT NULL,
|
|
758
|
-
autonomy_level TEXT NOT NULL,
|
|
759
|
-
status TEXT NOT NULL,
|
|
760
|
-
window_days INTEGER NOT NULL,
|
|
761
|
-
factors_json TEXT,
|
|
762
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
763
|
-
);
|
|
764
|
-
CREATE INDEX IF NOT EXISTS idx_autonomy_score_agent ON autonomy_score_snapshot(agent_name, created_at DESC);
|
|
765
|
-
|
|
766
|
-
CREATE TABLE IF NOT EXISTS project_focus (
|
|
767
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
768
|
-
project TEXT NOT NULL,
|
|
769
|
-
surface TEXT,
|
|
770
|
-
active_target TEXT,
|
|
771
|
-
focus_summary TEXT,
|
|
772
|
-
status TEXT NOT NULL DEFAULT 'active',
|
|
773
|
-
owner_agent TEXT,
|
|
774
|
-
coordinator_agent TEXT,
|
|
775
|
-
current_work_order_id INTEGER,
|
|
776
|
-
must_do_json TEXT,
|
|
777
|
-
must_not_do_json TEXT,
|
|
778
|
-
source_ref TEXT,
|
|
779
|
-
meta_json TEXT,
|
|
780
|
-
updated_by TEXT,
|
|
781
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
782
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
783
|
-
PRIMARY KEY(scope, project)
|
|
784
|
-
);
|
|
785
|
-
CREATE INDEX IF NOT EXISTS idx_project_focus_status ON project_focus(status, updated_at DESC);
|
|
786
|
-
|
|
787
|
-
CREATE TABLE IF NOT EXISTS project_task (
|
|
788
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
789
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
790
|
-
project TEXT NOT NULL,
|
|
791
|
-
surface TEXT,
|
|
792
|
-
title TEXT NOT NULL,
|
|
793
|
-
summary TEXT,
|
|
794
|
-
category TEXT,
|
|
795
|
-
priority TEXT NOT NULL DEFAULT 'normal',
|
|
796
|
-
status TEXT NOT NULL DEFAULT 'open',
|
|
797
|
-
owner_agent TEXT,
|
|
798
|
-
assigned_agent TEXT,
|
|
799
|
-
source_kind TEXT,
|
|
800
|
-
source_id TEXT,
|
|
801
|
-
source_ref TEXT,
|
|
802
|
-
acceptance_json TEXT,
|
|
803
|
-
blockers_json TEXT,
|
|
804
|
-
evidence_json TEXT,
|
|
805
|
-
linked_work_order_id INTEGER,
|
|
806
|
-
meta_json TEXT,
|
|
807
|
-
created_by TEXT,
|
|
808
|
-
updated_by TEXT,
|
|
809
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
810
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
811
|
-
completed_at TEXT
|
|
812
|
-
);
|
|
813
|
-
CREATE INDEX IF NOT EXISTS idx_project_task_project ON project_task(project, status, priority, updated_at DESC);
|
|
814
|
-
CREATE INDEX IF NOT EXISTS idx_project_task_assigned ON project_task(assigned_agent, status, updated_at DESC);
|
|
815
|
-
CREATE INDEX IF NOT EXISTS idx_project_task_source ON project_task(source_kind, source_id);
|
|
816
|
-
|
|
817
|
-
CREATE TABLE IF NOT EXISTS project_task_ingest (
|
|
818
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
819
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
820
|
-
source_kind TEXT NOT NULL,
|
|
821
|
-
source_id TEXT NOT NULL,
|
|
822
|
-
dedupe_key TEXT NOT NULL,
|
|
823
|
-
task_id INTEGER NOT NULL,
|
|
824
|
-
project TEXT,
|
|
825
|
-
brief_id INTEGER,
|
|
826
|
-
status TEXT NOT NULL DEFAULT 'linked',
|
|
827
|
-
meta_json TEXT,
|
|
828
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
829
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
830
|
-
);
|
|
831
|
-
CREATE UNIQUE INDEX IF NOT EXISTS idx_project_task_ingest_source ON project_task_ingest(scope, source_kind, source_id);
|
|
832
|
-
CREATE INDEX IF NOT EXISTS idx_project_task_ingest_dedupe ON project_task_ingest(scope, dedupe_key, updated_at DESC);
|
|
833
|
-
CREATE INDEX IF NOT EXISTS idx_project_task_ingest_task ON project_task_ingest(task_id);
|
|
834
|
-
CREATE INDEX IF NOT EXISTS idx_project_task_ingest_brief ON project_task_ingest(brief_id);
|
|
835
|
-
|
|
836
|
-
CREATE TABLE IF NOT EXISTS user_intent (
|
|
837
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
838
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
839
|
-
project TEXT,
|
|
840
|
-
user_name TEXT,
|
|
841
|
-
source_channel TEXT,
|
|
842
|
-
message_ref TEXT,
|
|
843
|
-
intent_kind TEXT NOT NULL DEFAULT 'request',
|
|
844
|
-
summary TEXT NOT NULL,
|
|
845
|
-
exact_words TEXT,
|
|
846
|
-
priority TEXT NOT NULL DEFAULT 'normal',
|
|
847
|
-
status TEXT NOT NULL DEFAULT 'captured',
|
|
848
|
-
linked_task_id INTEGER,
|
|
849
|
-
linked_work_order_id INTEGER,
|
|
850
|
-
meta_json TEXT,
|
|
851
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
852
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
853
|
-
);
|
|
854
|
-
CREATE INDEX IF NOT EXISTS idx_user_intent_project ON user_intent(project, status, created_at DESC);
|
|
855
|
-
CREATE INDEX IF NOT EXISTS idx_user_intent_status ON user_intent(status, priority, created_at DESC);
|
|
856
|
-
|
|
857
|
-
CREATE TABLE IF NOT EXISTS project_channel_policy (
|
|
858
|
-
scope TEXT NOT NULL DEFAULT 'default',
|
|
859
|
-
project TEXT NOT NULL,
|
|
860
|
-
telegram_role TEXT,
|
|
861
|
-
brief_role TEXT,
|
|
862
|
-
work_order_role TEXT,
|
|
863
|
-
rules_json TEXT,
|
|
864
|
-
status TEXT NOT NULL DEFAULT 'active',
|
|
865
|
-
meta_json TEXT,
|
|
866
|
-
updated_by TEXT,
|
|
867
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
868
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
869
|
-
PRIMARY KEY(scope, project)
|
|
870
|
-
);
|
|
871
|
-
CREATE INDEX IF NOT EXISTS idx_project_channel_policy_status ON project_channel_policy(status, updated_at DESC);
|
|
872
|
-
`);
|
|
873
|
-
try {
|
|
874
|
-
const cols = new Set(db.prepare("PRAGMA table_info(portal_context)").all().map((col) => col.name));
|
|
875
|
-
const add = (name, type) => { if (!cols.has(name)) db.exec(`ALTER TABLE portal_context ADD COLUMN ${name} ${type}`); };
|
|
876
|
-
add("portal_id", "TEXT");
|
|
877
|
-
add("portal_name", "TEXT");
|
|
878
|
-
add("environment", "TEXT");
|
|
879
|
-
add("country_or_market_json", "TEXT");
|
|
880
|
-
add("supported_languages_json", "TEXT");
|
|
881
|
-
add("billing_owner", "TEXT");
|
|
882
|
-
add("auth_owner", "TEXT");
|
|
883
|
-
add("deployment_owner", "TEXT");
|
|
884
|
-
add("legal_owner", "TEXT");
|
|
885
|
-
add("forbidden_cross_portal_leaks_json", "TEXT");
|
|
886
|
-
add("shared_modules_json", "TEXT");
|
|
887
|
-
add("protected_surfaces_json", "TEXT");
|
|
888
|
-
} catch {}
|
|
889
|
-
try {
|
|
890
|
-
db.exec(`
|
|
891
|
-
CREATE TRIGGER IF NOT EXISTS mnemo_journal_work_order_ai AFTER INSERT ON work_order BEGIN
|
|
892
|
-
INSERT INTO mnemo_event_journal
|
|
893
|
-
(source, channel, direction, actor, event_kind, ref_kind, ref_id, status, content, payload_json, meta_json, occurred_at)
|
|
894
|
-
VALUES
|
|
895
|
-
('work_order', NEW.project, 'internal', NEW.created_by, 'work_order_insert', 'work_order', CAST(NEW.id AS TEXT), NEW.status, NEW.title || ': ' || NEW.objective, NEW.allowed_resources_json, NEW.meta_json, NEW.created_at);
|
|
896
|
-
END;
|
|
897
|
-
CREATE TRIGGER IF NOT EXISTS mnemo_journal_capability_token_ai AFTER INSERT ON capability_token BEGIN
|
|
898
|
-
INSERT INTO mnemo_event_journal
|
|
899
|
-
(source, channel, direction, actor, event_kind, ref_kind, ref_id, status, content, payload_json, meta_json, occurred_at)
|
|
900
|
-
VALUES
|
|
901
|
-
('capability_token', NEW.project, 'internal', NEW.granted_by, 'capability_token_insert', 'capability_token', NEW.token_id, NEW.status, COALESCE(NEW.reason, ''), NEW.allowed_resources_json, NEW.meta_json, NEW.issued_at);
|
|
902
|
-
END;
|
|
903
|
-
CREATE TRIGGER IF NOT EXISTS mnemo_journal_capability_audit_ai AFTER INSERT ON capability_token_audit BEGIN
|
|
904
|
-
INSERT INTO mnemo_event_journal
|
|
905
|
-
(source, channel, direction, actor, event_kind, ref_kind, ref_id, status, content, payload_json, meta_json, occurred_at)
|
|
906
|
-
VALUES
|
|
907
|
-
('capability_token_audit', NEW.project, 'internal', NEW.agent_name, 'capability_token_check', 'capability_token_audit', CAST(NEW.id AS TEXT), CASE WHEN NEW.granted=1 THEN 'granted' ELSE 'blocked' END, NEW.reason, NEW.action_payload_json, NEW.matched_scope_json, NEW.created_at);
|
|
908
|
-
END;
|
|
909
|
-
CREATE TRIGGER IF NOT EXISTS mnemo_journal_context_snapshot_ai AFTER INSERT ON context_snapshot BEGIN
|
|
910
|
-
INSERT INTO mnemo_event_journal
|
|
911
|
-
(source, channel, direction, actor, event_kind, ref_kind, ref_id, status, content, payload_json, meta_json, occurred_at)
|
|
912
|
-
VALUES
|
|
913
|
-
('context_snapshot', NEW.project, 'internal', NEW.agent_name, 'context_snapshot_create', 'context_snapshot', CAST(NEW.id AS TEXT), NEW.status, COALESCE(NEW.title, NEW.summary, ''), NEW.remaining_work_json, NEW.meta_json, NEW.created_at);
|
|
914
|
-
END;
|
|
915
|
-
CREATE TRIGGER IF NOT EXISTS mnemo_journal_quality_gate_run_ai AFTER INSERT ON quality_gate_run BEGIN
|
|
916
|
-
INSERT INTO mnemo_event_journal
|
|
917
|
-
(source, channel, direction, actor, event_kind, ref_kind, ref_id, status, content, payload_json, meta_json, occurred_at)
|
|
918
|
-
VALUES
|
|
919
|
-
('quality_gate', NEW.project, 'internal', NEW.agent_name, 'quality_gate_run', 'quality_gate_run', CAST(NEW.id AS TEXT), NEW.status, NEW.gate_id, NEW.evidence_json, NEW.meta_json, NEW.created_at);
|
|
920
|
-
END;
|
|
921
|
-
CREATE TRIGGER IF NOT EXISTS mnemo_journal_project_focus_au AFTER UPDATE ON project_focus BEGIN
|
|
922
|
-
INSERT INTO mnemo_event_journal
|
|
923
|
-
(source, channel, direction, actor, event_kind, ref_kind, ref_id, status, content, payload_json, meta_json, occurred_at)
|
|
924
|
-
VALUES
|
|
925
|
-
('project_focus', NEW.project, 'internal', NEW.updated_by, 'project_focus_update', 'project_focus', NEW.project, NEW.status, COALESCE(NEW.focus_summary, NEW.active_target, ''), NEW.must_do_json, NEW.meta_json, NEW.updated_at);
|
|
926
|
-
END;
|
|
927
|
-
CREATE TRIGGER IF NOT EXISTS mnemo_journal_project_task_ai AFTER INSERT ON project_task BEGIN
|
|
928
|
-
INSERT INTO mnemo_event_journal
|
|
929
|
-
(source, channel, direction, actor, event_kind, ref_kind, ref_id, status, content, payload_json, meta_json, occurred_at)
|
|
930
|
-
VALUES
|
|
931
|
-
('project_task', NEW.project, 'internal', NEW.created_by, 'project_task_insert', 'project_task', CAST(NEW.id AS TEXT), NEW.status, NEW.title, NEW.acceptance_json, NEW.meta_json, NEW.created_at);
|
|
932
|
-
END;
|
|
933
|
-
CREATE TRIGGER IF NOT EXISTS mnemo_journal_user_intent_ai AFTER INSERT ON user_intent BEGIN
|
|
934
|
-
INSERT INTO mnemo_event_journal
|
|
935
|
-
(source, channel, direction, actor, event_kind, ref_kind, ref_id, status, content, payload_json, meta_json, occurred_at)
|
|
936
|
-
VALUES
|
|
937
|
-
('user_intent', NEW.project, 'inbound', NEW.user_name, 'user_intent_capture', 'user_intent', CAST(NEW.id AS TEXT), NEW.status, NEW.summary, NEW.exact_words, NEW.meta_json, NEW.created_at);
|
|
938
|
-
END;
|
|
939
|
-
CREATE TRIGGER IF NOT EXISTS mnemo_journal_agent_role_assignment_ai AFTER INSERT ON agent_role_assignment BEGIN
|
|
940
|
-
INSERT INTO mnemo_event_journal
|
|
941
|
-
(source, channel, direction, actor, event_kind, ref_kind, ref_id, status, content, payload_json, meta_json, occurred_at)
|
|
942
|
-
VALUES
|
|
943
|
-
('agent_role', NEW.project, 'internal', NEW.selected_by, 'agent_role_select', 'agent_role_assignment', CAST(NEW.id AS TEXT), NEW.status, NEW.agent_name || ' -> ' || NEW.role_name, NEW.plan_summary, NEW.meta_json, NEW.selected_at);
|
|
944
|
-
END;
|
|
945
|
-
CREATE TRIGGER IF NOT EXISTS mnemo_journal_portal_context_ai AFTER INSERT ON portal_context BEGIN
|
|
946
|
-
INSERT INTO mnemo_event_journal
|
|
947
|
-
(source, channel, direction, actor, event_kind, ref_kind, ref_id, status, content, payload_json, meta_json, occurred_at)
|
|
948
|
-
VALUES
|
|
949
|
-
('portal_context', NEW.project, 'internal', NEW.updated_by, 'portal_context_set', 'portal_context', CAST(NEW.id AS TEXT), NEW.status, NEW.project || ':' || NEW.portal || ' ' || COALESCE(NEW.brand_name, ''), NEW.portal_rules_json, NEW.meta_json, NEW.created_at);
|
|
950
|
-
END;
|
|
951
|
-
CREATE TRIGGER IF NOT EXISTS mnemo_journal_workflow_receipt_ai AFTER INSERT ON agent_workflow_receipt BEGIN
|
|
952
|
-
INSERT INTO mnemo_event_journal
|
|
953
|
-
(source, channel, direction, actor, event_kind, ref_kind, ref_id, status, content, payload_json, meta_json, occurred_at)
|
|
954
|
-
VALUES
|
|
955
|
-
('workflow_receipt', NEW.project, 'internal', NEW.agent_name, 'workflow_receipt', 'agent_workflow_receipt', CAST(NEW.id AS TEXT), NEW.status, NEW.phase || ': ' || NEW.summary, NEW.evidence_json, NEW.meta_json, NEW.created_at);
|
|
956
|
-
END;
|
|
957
|
-
CREATE TRIGGER IF NOT EXISTS mnemo_journal_rule_violation_ai AFTER INSERT ON rule_violation_log BEGIN
|
|
958
|
-
INSERT INTO mnemo_event_journal
|
|
959
|
-
(source, channel, direction, actor, event_kind, ref_kind, ref_id, status, content, payload_json, meta_json, occurred_at)
|
|
960
|
-
VALUES
|
|
961
|
-
('rule_violation', NEW.project, 'internal', NEW.agent_name, 'rule_violation_log', 'rule_violation_log', CAST(NEW.id AS TEXT), NEW.status, NEW.rule_key || ': ' || COALESCE(NEW.rule_text, ''), NEW.evidence_json, NEW.meta_json, NEW.created_at);
|
|
962
|
-
END;
|
|
963
|
-
CREATE TRIGGER IF NOT EXISTS mnemo_journal_owner_rule_snapshot_ai AFTER INSERT ON owner_rule_snapshot BEGIN
|
|
964
|
-
INSERT INTO mnemo_event_journal
|
|
965
|
-
(source, channel, direction, actor, event_kind, ref_kind, ref_id, status, content, payload_json, meta_json, occurred_at)
|
|
966
|
-
VALUES
|
|
967
|
-
('owner_rule_snapshot', NEW.project, 'internal', NEW.created_by, 'owner_rule_snapshot', 'owner_rule_snapshot', CAST(NEW.id AS TEXT), 'captured', NEW.project || ' rules hash ' || NEW.rules_hash, NEW.rules_json, NEW.meta_json, NEW.created_at);
|
|
968
|
-
END;
|
|
969
|
-
CREATE TRIGGER IF NOT EXISTS mnemo_journal_task_fingerprint_ai AFTER INSERT ON task_fingerprint BEGIN
|
|
970
|
-
INSERT INTO mnemo_event_journal
|
|
971
|
-
(source, channel, direction, actor, event_kind, ref_kind, ref_id, status, content, payload_json, meta_json, occurred_at)
|
|
972
|
-
VALUES
|
|
973
|
-
('task_fingerprint', NEW.project, 'internal', NEW.agent_name, 'task_fingerprint', 'task_fingerprint', CAST(NEW.id AS TEXT), NEW.status, NEW.summary, NEW.fingerprint, NEW.meta_json, NEW.created_at);
|
|
974
|
-
END;
|
|
975
|
-
`);
|
|
976
|
-
} catch {}
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
function normalizeAllowedResources(input = {}) {
|
|
980
|
-
const direct = input.allowed_resources || input.resources_scope || input.resource_scope;
|
|
981
|
-
const parsed = parseMaybeJson(direct, direct);
|
|
982
|
-
const src = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : input;
|
|
983
|
-
const out = [];
|
|
984
|
-
const add = (kind, key, label) => {
|
|
985
|
-
const k = normalizeResourceKind(kind);
|
|
986
|
-
const normalized = normalizeResourceKey(k, key);
|
|
987
|
-
if (!normalized) return;
|
|
988
|
-
out.push({ resource_kind: k, resource_key: normalized, label: label || key });
|
|
989
|
-
};
|
|
990
|
-
for (const file of listInput(src.files)) add("file", file, file);
|
|
991
|
-
for (const route of listInput(src.routes)) add("route", route, route);
|
|
992
|
-
for (const domain of listInput(src.domains)) add("domain", domain, domain);
|
|
993
|
-
for (const system of listInput(src.system_names || src.systems)) add("system", system, system);
|
|
994
|
-
for (const item of Array.isArray(src.resources) ? src.resources : []) {
|
|
995
|
-
if (!item || typeof item !== "object") continue;
|
|
996
|
-
add(item.resource_kind || item.kind, item.resource_key || item.key || item.file_path || item.route || item.domain || item.system_name, item.label);
|
|
997
|
-
}
|
|
998
|
-
if (src.allow_all === true || src.all === true || listInput(src.resources).includes("*")) {
|
|
999
|
-
out.push({ resource_kind: "*", resource_key: "*", label: "all" });
|
|
1000
|
-
}
|
|
1001
|
-
const seen = new Set();
|
|
1002
|
-
return out.filter((item) => {
|
|
1003
|
-
const key = item.resource_kind + ":" + item.resource_key;
|
|
1004
|
-
if (seen.has(key)) return false;
|
|
1005
|
-
seen.add(key);
|
|
1006
|
-
return true;
|
|
1007
|
-
});
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
function requestedResources(input = {}) {
|
|
1011
|
-
return normalizeAllowedResources(input);
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
function normalizeTools(value) {
|
|
1015
|
-
const tools = listInput(value);
|
|
1016
|
-
return tools.length ? tools : [];
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
function rowToCharter(row) {
|
|
1020
|
-
return row ? Object.assign({}, row, {
|
|
1021
|
-
responsibilities: parseJson(row.responsibilities_json, []),
|
|
1022
|
-
boundaries: parseJson(row.boundaries_json, []),
|
|
1023
|
-
standard_permissions: parseJson(row.standard_permissions_json, []),
|
|
1024
|
-
allowed_resources: parseJson(row.allowed_resources_json, []),
|
|
1025
|
-
escalation_rules: parseJson(row.escalation_rules_json, []),
|
|
1026
|
-
standing_permissions: parseJson(row.standing_permissions_json, []),
|
|
1027
|
-
meta: parseJson(row.meta_json, {}),
|
|
1028
|
-
}) : null;
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
function departmentCharterSet(db, input = {}) {
|
|
1032
|
-
ensureAgentGovernanceSchema(db);
|
|
1033
|
-
const scope = scopeName(input.scope);
|
|
1034
|
-
const department = normalizeDepartment(input.department_name || input.department);
|
|
1035
|
-
const allowed = normalizeAllowedResources(input);
|
|
1036
|
-
db.prepare(`
|
|
1037
|
-
INSERT INTO department_charter
|
|
1038
|
-
(scope, department_name, mission, responsibilities_json, boundaries_json, standard_permissions_json, allowed_resources_json, escalation_rules_json, standing_permissions_json, autonomy_floor, autonomy_ceiling, default_risk_class, lead_agent, review_agent, status, meta_json, updated_by, updated_at)
|
|
1039
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
1040
|
-
ON CONFLICT(scope, department_name) DO UPDATE SET
|
|
1041
|
-
mission=excluded.mission,
|
|
1042
|
-
responsibilities_json=excluded.responsibilities_json,
|
|
1043
|
-
boundaries_json=excluded.boundaries_json,
|
|
1044
|
-
standard_permissions_json=excluded.standard_permissions_json,
|
|
1045
|
-
allowed_resources_json=excluded.allowed_resources_json,
|
|
1046
|
-
escalation_rules_json=excluded.escalation_rules_json,
|
|
1047
|
-
standing_permissions_json=excluded.standing_permissions_json,
|
|
1048
|
-
autonomy_floor=excluded.autonomy_floor,
|
|
1049
|
-
autonomy_ceiling=excluded.autonomy_ceiling,
|
|
1050
|
-
default_risk_class=excluded.default_risk_class,
|
|
1051
|
-
lead_agent=excluded.lead_agent,
|
|
1052
|
-
review_agent=excluded.review_agent,
|
|
1053
|
-
status=excluded.status,
|
|
1054
|
-
meta_json=excluded.meta_json,
|
|
1055
|
-
updated_by=excluded.updated_by,
|
|
1056
|
-
updated_at=excluded.updated_at
|
|
1057
|
-
`).run(
|
|
1058
|
-
scope,
|
|
1059
|
-
department,
|
|
1060
|
-
textOrNull(input.mission),
|
|
1061
|
-
safeJson(listInput(input.responsibilities), []),
|
|
1062
|
-
safeJson(listInput(input.boundaries), []),
|
|
1063
|
-
safeJson(listInput(input.standard_permissions), []),
|
|
1064
|
-
safeJson(allowed, []),
|
|
1065
|
-
safeJson(listInput(input.escalation_rules), []),
|
|
1066
|
-
safeJson(listInput(input.standing_permissions), []),
|
|
1067
|
-
clampInt(input.autonomy_floor, 0, 0, 5),
|
|
1068
|
-
clampInt(input.autonomy_ceiling, 3, 0, 5),
|
|
1069
|
-
normalizeRisk(input.default_risk_class),
|
|
1070
|
-
normalizeAgentName(input.lead_agent || "") || null,
|
|
1071
|
-
normalizeAgentName(input.review_agent || "") || null,
|
|
1072
|
-
input.status || "active",
|
|
1073
|
-
safeJson(input.meta || {}, {}),
|
|
1074
|
-
normalizeAgentName(input.updated_by || input.agent_name || "") || null
|
|
1075
|
-
);
|
|
1076
|
-
return { ok: true, charter: departmentCharterGet(db, { scope, department_name: department }).charter };
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
function departmentCharterGet(db, input = {}) {
|
|
1080
|
-
ensureAgentGovernanceSchema(db);
|
|
1081
|
-
const row = db.prepare("SELECT * FROM department_charter WHERE scope=? AND department_name=?").get(scopeName(input.scope), normalizeDepartment(input.department_name || input.department));
|
|
1082
|
-
return { ok: !!row, charter: rowToCharter(row) };
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
function departmentCharterList(db, input = {}) {
|
|
1086
|
-
ensureAgentGovernanceSchema(db);
|
|
1087
|
-
const where = ["scope=?"];
|
|
1088
|
-
const params = [scopeName(input.scope)];
|
|
1089
|
-
if (input.status) { where.push("status=?"); params.push(input.status); }
|
|
1090
|
-
params.push(clampInt(input.limit, 100, 1, 500));
|
|
1091
|
-
const rows = db.prepare(`SELECT * FROM department_charter WHERE ${where.join(" AND ")} ORDER BY department_name ASC LIMIT ?`).all(...params).map(rowToCharter);
|
|
1092
|
-
return { ok: true, count: rows.length, charters: rows };
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
function expiresAt(input = {}) {
|
|
1096
|
-
if (input.expires_at) return new Date(input.expires_at).toISOString();
|
|
1097
|
-
const ttl = clampInt(input.ttl_minutes, 240, 1, 43200);
|
|
1098
|
-
return new Date(Date.now() + ttl * 60000).toISOString();
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
function rowToWorkOrder(row) {
|
|
1102
|
-
return row ? Object.assign({}, row, {
|
|
1103
|
-
allowed_tools: parseJson(row.allowed_tools_json, []),
|
|
1104
|
-
allowed_resources: parseJson(row.allowed_resources_json, []),
|
|
1105
|
-
done_criteria: parseJson(row.done_criteria_json, []),
|
|
1106
|
-
required_evidence: parseJson(row.required_evidence_json, []),
|
|
1107
|
-
approval_ids: parseJson(row.approval_ids_json, []),
|
|
1108
|
-
evidence: parseJson(row.evidence_json, []),
|
|
1109
|
-
meta: parseJson(row.meta_json, {}),
|
|
1110
|
-
}) : null;
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
function rowToToken(row) {
|
|
1114
|
-
return row ? Object.assign({}, row, {
|
|
1115
|
-
allowed_tools: parseJson(row.allowed_tools_json, []),
|
|
1116
|
-
allowed_resources: parseJson(row.allowed_resources_json, []),
|
|
1117
|
-
required_evidence: parseJson(row.required_evidence_json, []),
|
|
1118
|
-
approval_ids: parseJson(row.approval_ids_json, []),
|
|
1119
|
-
budgets: parseJson(row.budgets_json, {}),
|
|
1120
|
-
meta: parseJson(row.meta_json, {}),
|
|
1121
|
-
}) : null;
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
const AGENT_NEUTRAL_RUNTIME_CONTRACT = Object.freeze({
|
|
1125
|
-
contract_version: "mnemo-agent-neutral-v1",
|
|
1126
|
-
agent_neutral: true,
|
|
1127
|
-
rule: "All runtimes use the same Mnemo tools. Runtime adapters translate their local tool names into this contract; Mnemo remains the authority.",
|
|
1128
|
-
required_flow: [
|
|
1129
|
-
"mem_session_start + mem_context_preview at session start",
|
|
1130
|
-
"mem_work_order_create_from_template or mem_work_order_create before scoped work",
|
|
1131
|
-
"sync current repo/live state and post a shared team work notice before code edits",
|
|
1132
|
-
"mem_capability_token_check or mem_runtime_tool_receipt_start before risky non-read actions",
|
|
1133
|
-
"mem_runtime_tool_receipt_finish after tool execution with concrete evidence",
|
|
1134
|
-
"mem_context_snapshot_create before handoff, compaction, or long pause",
|
|
1135
|
-
"mem_quality_gate_run and mem_work_order_complete before claiming done"
|
|
1136
|
-
],
|
|
1137
|
-
low_risk_without_token: ["read", "inspect", "list"],
|
|
1138
|
-
risky_requires_token: ["code_edit", "write", "delete", "deploy", "external_comm", "auth", "billing", "production"]
|
|
1139
|
-
});
|
|
1140
|
-
|
|
1141
|
-
const COMPANY_AGENT_EXECUTION_GOVERNANCE_GATE_ID = "company_agent_execution_governance_v1";
|
|
1142
|
-
const COMPANY_AGENT_EXECUTION_GOVERNANCE_VERSION = "2026-06-06";
|
|
1143
|
-
const PREMORTEM_GATE_ID = "premortem_gate";
|
|
1144
|
-
const ANGEL_MOCKUP_GATE_ID = "angel_mockup_gate";
|
|
1145
|
-
const PREMORTEM_REQUIRED_EVIDENCE = Object.freeze([
|
|
1146
|
-
"premortem context what who success",
|
|
1147
|
-
"premortem failure reasons",
|
|
1148
|
-
"premortem revised plan checklist",
|
|
1149
|
-
]);
|
|
1150
|
-
const ANGEL_MOCKUP_REQUIRED_EVIDENCE = Object.freeze([
|
|
1151
|
-
"mayk approved angel mockup",
|
|
1152
|
-
"angel mockup source reference",
|
|
1153
|
-
"implementation screenshot desktop mobile",
|
|
1154
|
-
"angel mockup qa pass",
|
|
1155
|
-
]);
|
|
1156
|
-
|
|
1157
|
-
const COMPANY_AGENT_EXECUTION_GOVERNANCE_POINTS = Object.freeze([
|
|
1158
|
-
{ id: "0", title: "Runtime-Zwang", rule: "Editieren, Deployen und done/fertig melden wird technisch geblockt, wenn Preflight/Postflight fehlt." },
|
|
1159
|
-
{ id: "1", title: "Valid Work Order", rule: "Ohne gueltige Work Order kein Start, keine Datei- oder Routen-Aenderung." },
|
|
1160
|
-
{ id: "1a", title: "Live-Sync + Team-Hinweis", rule: "Vor jeder Code-Arbeit aktuellen Live-/Repo-Stand ziehen oder synchronisieren und im gemeinsamen Teamchat kurz melden: Agent, Projekt, Surface und Dateien/Bereich. Bei gleicher Surface durch anderen Bot nicht parallel editieren." },
|
|
1161
|
-
{ id: "2", title: "Route-Quelle-Matrix", rule: "Ein Ziel hat genau eine Route und genau eine Renderer-/Komponenten-Quelle." },
|
|
1162
|
-
{ id: "3", title: "Single-Source + Owner", rule: "Geteilte Shells, Sidebars, Header, Footer und Komponenten haben eine Quelle und einen Owner." },
|
|
1163
|
-
{ id: "4", title: "Mockup/Spec + alle States", rule: "UI-Builds brauchen freigegebenes Mockup/Spec inklusive leer, loading, error und full state." },
|
|
1164
|
-
{ id: "5", title: "Expliziter Datei-/Routen-Scope", rule: "Builds duerfen nur freigegebene Dateien und Routen anfassen; Neben- oder Bulk-Aenderungen blockieren." },
|
|
1165
|
-
{ id: "6", title: "Cleanup-Audit vor Neubau", rule: "Vor Neubau alte Routen, Shells, Komponenten, Router, Mounts, CTAs und Redirects inventarisieren." },
|
|
1166
|
-
{ id: "7", title: "Altcode raus", rule: "Ersetzter Code wird geloescht oder ausserhalb des Live-Pfads archiviert; grep/Route-Scan muss 0 renderbare Altstaende zeigen." },
|
|
1167
|
-
{ id: "8", title: "Backup-Lifecycle", rule: "Backup vor riskanter Aenderung; nach Freigabe/stabilem Lauf keine .bak/Kopien/Shadow-Dateien im Live-Projekt." },
|
|
1168
|
-
{ id: "9", title: "Step-by-step Build", rule: "Eine Stufe nach der anderen in Menue-/Logik-Reihenfolge; keine parallelen Bulk-Builds." },
|
|
1169
|
-
{ id: "10", title: "Design QA maschinell", rule: "Keine Container-Hover, blauen Raender/Glows, Lila, Emojis; Collapse-Gap 0; keine Card-in-Card." },
|
|
1170
|
-
{ id: "11", title: "Copy/i18n QA", rule: "Keine internen Texte; echte Umlaute; alle Sprachen; keine ASCII-Reste oder Maschinen-Kauderwelsch." },
|
|
1171
|
-
{ id: "12", title: "Daten/Honesty QA", rule: "Keine falschen Claims, toten Links, Public-Source-URLs oder Registry-Leaks." },
|
|
1172
|
-
{ id: "13", title: "Postflight Evidence", rule: "Kein gruen ohne grep=0, Route-Check, Cross-Page-Paritaet, no-store/cache-busted Reload und Desktop/Mobile/Collapsed-Belege." },
|
|
1173
|
-
{ id: "14", title: "Freeze bei Regelbruch", rule: "Bei Gate-Umgehung oder done ohne Belege wird die Surface eingefroren, bis Ursache und Praevention dokumentiert sind." },
|
|
1174
|
-
{ id: "15", title: "Runtime-/Mount-Gate", rule: "Vor gruen pruefen, was wirklich laeuft: PM2/Service, Entry-Point, Router/Mounts, Imports/Requires, Shadow-Apps, Nginx/Proxy und Live-URL." },
|
|
1175
|
-
]);
|
|
1176
|
-
|
|
1177
|
-
const COMPANY_AGENT_EXECUTION_GOVERNANCE_REQUIRED_EVIDENCE = Object.freeze([
|
|
1178
|
-
"governance runtime enforcement ack",
|
|
1179
|
-
"valid work order id",
|
|
1180
|
-
"live repo sync and team work notice",
|
|
1181
|
-
"route source matrix",
|
|
1182
|
-
"single source owner scan",
|
|
1183
|
-
"approved mockup spec all states",
|
|
1184
|
-
"approved file route scope",
|
|
1185
|
-
"cleanup audit before build",
|
|
1186
|
-
"alt code route scan zero live",
|
|
1187
|
-
"backup lifecycle proof",
|
|
1188
|
-
"no bulk build step plan",
|
|
1189
|
-
"design qa rendered css visual checks",
|
|
1190
|
-
"i18n copy umlaut language scan",
|
|
1191
|
-
"data honesty public claims links check",
|
|
1192
|
-
"postflight evidence package",
|
|
1193
|
-
"rule breach freeze check",
|
|
1194
|
-
"runtime mount gate proof",
|
|
1195
|
-
...PREMORTEM_REQUIRED_EVIDENCE,
|
|
1196
|
-
]);
|
|
1197
|
-
|
|
1198
|
-
const DEPLOY_PRECHECK_GATE_ID = "deploy_precheck_gate";
|
|
1199
|
-
const DEPLOY_PRECHECK_REQUIRED_EVIDENCE = Object.freeze([
|
|
1200
|
-
"tests",
|
|
1201
|
-
"diff reviewed",
|
|
1202
|
-
"pre deploy check result",
|
|
1203
|
-
"old version fallback stale live scan",
|
|
1204
|
-
"restore rollback point",
|
|
1205
|
-
"accepted version backup outside live path",
|
|
1206
|
-
]);
|
|
1207
|
-
const DEPLOY_REQUIRED_EVIDENCE = Object.freeze([
|
|
1208
|
-
...DEPLOY_PRECHECK_REQUIRED_EVIDENCE,
|
|
1209
|
-
"deploy manifest who what when where how",
|
|
1210
|
-
"deploy or push result",
|
|
1211
|
-
"production health check",
|
|
1212
|
-
]);
|
|
1213
|
-
|
|
1214
|
-
function isDeployAction(input = {}) {
|
|
1215
|
-
const action = String(input.action_type || input.action || input.action_kind || "").toLowerCase();
|
|
1216
|
-
if (["deploy", "production", "ship", "release"].includes(action)) return true;
|
|
1217
|
-
const text = [
|
|
1218
|
-
input.task,
|
|
1219
|
-
input.summary,
|
|
1220
|
-
input.objective,
|
|
1221
|
-
input.title,
|
|
1222
|
-
input.tool_name,
|
|
1223
|
-
input.command,
|
|
1224
|
-
input.system_name,
|
|
1225
|
-
].filter(Boolean).join(" ").toLowerCase().replace(/[_-]+/g, " ");
|
|
1226
|
-
return /\b(deploy|deployment|pm2|nginx|systemctl|restart|git push|ssh|scp|rsync)\b/.test(text);
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
function companyGovernanceRequired(input = {}) {
|
|
1230
|
-
if (input.company_governance_required === false) return false;
|
|
1231
|
-
if (input.skip_company_governance === true) return false;
|
|
1232
|
-
return requiresCapabilityToken(input);
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
function companyGovernanceMeta(required) {
|
|
1236
|
-
if (!required) return {};
|
|
1237
|
-
return {
|
|
1238
|
-
gate_id: COMPANY_AGENT_EXECUTION_GOVERNANCE_GATE_ID,
|
|
1239
|
-
version: COMPANY_AGENT_EXECUTION_GOVERNANCE_VERSION,
|
|
1240
|
-
hard_block: true,
|
|
1241
|
-
points: COMPANY_AGENT_EXECUTION_GOVERNANCE_POINTS,
|
|
1242
|
-
required_evidence: COMPANY_AGENT_EXECUTION_GOVERNANCE_REQUIRED_EVIDENCE,
|
|
1243
|
-
};
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
function withCompanyGovernanceRequiredEvidence(requiredEvidence, input = {}) {
|
|
1247
|
-
let required = listInput(requiredEvidence);
|
|
1248
|
-
if (isDeployAction(input)) required = required.concat(DEPLOY_REQUIRED_EVIDENCE);
|
|
1249
|
-
if (companyGovernanceRequired(input)) required = required.concat(COMPANY_AGENT_EXECUTION_GOVERNANCE_REQUIRED_EVIDENCE);
|
|
1250
|
-
if (requiresAngelMockupGate(input)) required = required.concat(ANGEL_MOCKUP_REQUIRED_EVIDENCE);
|
|
1251
|
-
return uniqueStrings(required);
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
function withCompanyGovernanceQualityGates(gates, input = {}) {
|
|
1255
|
-
let required = listInput(gates);
|
|
1256
|
-
if (isDeployAction(input)) required = required.concat([DEPLOY_PRECHECK_GATE_ID]);
|
|
1257
|
-
if (companyGovernanceRequired(input)) required = required.concat([COMPANY_AGENT_EXECUTION_GOVERNANCE_GATE_ID, PREMORTEM_GATE_ID]);
|
|
1258
|
-
if (requiresAngelMockupGate(input)) required = required.concat([ANGEL_MOCKUP_GATE_ID]);
|
|
1259
|
-
return uniqueStrings(required);
|
|
1260
|
-
}
|
|
1261
|
-
|
|
1262
|
-
function requiresAngelMockupGate(input = {}) {
|
|
1263
|
-
if (input.skip_angel_mockup_gate === true) return false;
|
|
1264
|
-
const meta = input.meta && typeof input.meta === "object" ? input.meta : {};
|
|
1265
|
-
if (meta.skip_angel_mockup_gate === true) return false;
|
|
1266
|
-
if (input.require_angel_mockup_gate === true || meta.require_angel_mockup_gate === true) return true;
|
|
1267
|
-
const project = normalizeProject(input.project || meta.project || "");
|
|
1268
|
-
if (project !== "apps.blun.ai:wizard2") return false;
|
|
1269
|
-
const text = [
|
|
1270
|
-
input.title,
|
|
1271
|
-
input.summary,
|
|
1272
|
-
input.objective,
|
|
1273
|
-
input.task,
|
|
1274
|
-
input.category,
|
|
1275
|
-
input.surface,
|
|
1276
|
-
input.action_type,
|
|
1277
|
-
(input.quality_gates || []).toString(),
|
|
1278
|
-
(meta.quality_gates || []).toString(),
|
|
1279
|
-
meta.template_id,
|
|
1280
|
-
].filter(Boolean).join(" ").toLowerCase().replace(/[_-]+/g, " ");
|
|
1281
|
-
if (text.includes(ANGEL_MOCKUP_GATE_ID.replace(/_/g, " "))) return true;
|
|
1282
|
-
return /\b(ui|frontend|front end|layout|design|mockup|css|mobile|desktop|responsive|component|button|composer|chat|sidebar|header|navbar|banner|dropdown|modal|card|form|table|ticket chat|credit leiste)\b/.test(text);
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
|
-
function companyGovernanceRequirementGaps(requiredEvidence) {
|
|
1286
|
-
const normalized = new Set(listInput(requiredEvidence).map(normalizeEvidenceText));
|
|
1287
|
-
return COMPANY_AGENT_EXECUTION_GOVERNANCE_REQUIRED_EVIDENCE
|
|
1288
|
-
.filter((item) => !normalized.has(normalizeEvidenceText(item)));
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
function deployRequirementGaps(requiredEvidence, input = {}) {
|
|
1292
|
-
if (!isDeployAction(input)) return [];
|
|
1293
|
-
const normalized = new Set(listInput(requiredEvidence).map(normalizeEvidenceText));
|
|
1294
|
-
return DEPLOY_REQUIRED_EVIDENCE.filter((item) => !normalized.has(normalizeEvidenceText(item)));
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
function companyGovernanceRulebook(input = {}) {
|
|
1298
|
-
const project = textOrNull(input.project, 240);
|
|
1299
|
-
const agent = normalizeAgentName(input.agent_name || input.agent || "");
|
|
1300
|
-
const task = textOrNull(input.task || input.summary || input.objective, 1200);
|
|
1301
|
-
const points = COMPANY_AGENT_EXECUTION_GOVERNANCE_POINTS.map((point) => Object.assign({}, point));
|
|
1302
|
-
const requiredEvidence = COMPANY_AGENT_EXECUTION_GOVERNANCE_REQUIRED_EVIDENCE.slice();
|
|
1303
|
-
const runtimeContract = Object.assign({}, AGENT_NEUTRAL_RUNTIME_CONTRACT, {
|
|
1304
|
-
company_governance_gate_id: COMPANY_AGENT_EXECUTION_GOVERNANCE_GATE_ID,
|
|
1305
|
-
company_governance_version: COMPANY_AGENT_EXECUTION_GOVERNANCE_VERSION,
|
|
1306
|
-
mandatory_for: [
|
|
1307
|
-
"all agents",
|
|
1308
|
-
"all projects",
|
|
1309
|
-
"all public repositories",
|
|
1310
|
-
"all private repositories",
|
|
1311
|
-
"all portals",
|
|
1312
|
-
"all code, UI, route, deploy, cleanup, and production work",
|
|
1313
|
-
],
|
|
1314
|
-
hard_blocks: [
|
|
1315
|
-
"No editor/write/delete/deploy without valid Work Order, route/source matrix, approved scope, and required preflight.",
|
|
1316
|
-
"No deploy/live restart/push without a passed pre-deploy check, stale/fallback live scan, accepted-version backup outside the live path, and rollback point.",
|
|
1317
|
-
"Every deploy must be documented with who, what, when, where, how, result, health check, and restore reference.",
|
|
1318
|
-
"No done/green/deploy without explicit evidence covering every required_evidence item.",
|
|
1319
|
-
"Old live routes, duplicate shells/components, stale mounts, and live-path backups are release blockers.",
|
|
1320
|
-
"Rule bypass freezes the affected surface until the missing gate is made blocking.",
|
|
1321
|
-
],
|
|
1322
|
-
});
|
|
1323
|
-
const agentProtocol = [
|
|
1324
|
-
"Read this rulebook at session start before build/code/deploy work.",
|
|
1325
|
-
"Create or attach a Work Order before scoped execution.",
|
|
1326
|
-
"Attach route source matrix, single-source/owner scan, mockup/spec states, approved file/route scope, cleanup audit, premortem, and runtime/mount plan before editing.",
|
|
1327
|
-
"Run mem_capability_token_check or mem_runtime_tool_receipt_start before risky tools.",
|
|
1328
|
-
"For deploy/live restart/push: attach passed pre-deploy check, stale/fallback scan, accepted-version backup, rollback point, and deploy manifest before executing.",
|
|
1329
|
-
"Run required quality gates and finish runtime receipts with concrete evidence.",
|
|
1330
|
-
"Call mem_work_order_complete only when every required_evidence item is covered by passing evidence.",
|
|
1331
|
-
"If a gate is missing, report blocked/needs_review instead of claiming done.",
|
|
1332
|
-
];
|
|
1333
|
-
const telegramText = [
|
|
1334
|
-
"Mnemo Governance Rulebook ist scharf:",
|
|
1335
|
-
`Gate: ${COMPANY_AGENT_EXECUTION_GOVERNANCE_GATE_ID} v${COMPANY_AGENT_EXECUTION_GOVERNANCE_VERSION}`,
|
|
1336
|
-
"Gilt fuer alle Agents, Projekte, Public/Private Repos und Portale.",
|
|
1337
|
-
"Harte Regel: Ohne Work Order + Scope + Route/Quelle + Evidence kein Edit/Deploy/Fertig.",
|
|
1338
|
-
"Punkte:",
|
|
1339
|
-
...points.map((point) => `${point.id}. ${point.title}: ${point.rule}`),
|
|
1340
|
-
"Pflicht-Belege:",
|
|
1341
|
-
...requiredEvidence.map((item) => `- ${item}`),
|
|
1342
|
-
].join("\n");
|
|
1343
|
-
return {
|
|
1344
|
-
ok: true,
|
|
1345
|
-
status: "active",
|
|
1346
|
-
hard_block: true,
|
|
1347
|
-
gate_id: COMPANY_AGENT_EXECUTION_GOVERNANCE_GATE_ID,
|
|
1348
|
-
version: COMPANY_AGENT_EXECUTION_GOVERNANCE_VERSION,
|
|
1349
|
-
project: project || null,
|
|
1350
|
-
agent_name: agent || null,
|
|
1351
|
-
task: task || null,
|
|
1352
|
-
point_count: points.length,
|
|
1353
|
-
points,
|
|
1354
|
-
required_evidence: requiredEvidence,
|
|
1355
|
-
quality_gates: [COMPANY_AGENT_EXECUTION_GOVERNANCE_GATE_ID, PREMORTEM_GATE_ID],
|
|
1356
|
-
runtime_contract: runtimeContract,
|
|
1357
|
-
agent_protocol: agentProtocol,
|
|
1358
|
-
templates: ["company_build_governance_v1"],
|
|
1359
|
-
telegram_text: telegramText,
|
|
1360
|
-
};
|
|
1361
|
-
}
|
|
1362
|
-
|
|
1363
|
-
function requiredEvidenceForOrder(order, input = {}) {
|
|
1364
|
-
const required = listInput(order && order.required_evidence || []);
|
|
1365
|
-
const checkInput = Object.assign({}, order || {}, input || {}, {
|
|
1366
|
-
action_type: (input && input.action_type) || (order && order.action_type),
|
|
1367
|
-
});
|
|
1368
|
-
return withCompanyGovernanceRequiredEvidence(required, checkInput);
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
|
-
const BUILTIN_WORK_ORDER_TEMPLATES = [
|
|
1372
|
-
{
|
|
1373
|
-
template_id: "company_build_governance_v1",
|
|
1374
|
-
title: "Company build governance hard gate",
|
|
1375
|
-
description: "Mandatory ExampleCorp-wide preflight/postflight contract for build, code, deploy, and public/private repo work.",
|
|
1376
|
-
department_name: "governance",
|
|
1377
|
-
risk_class: "production",
|
|
1378
|
-
action_type: "code_edit",
|
|
1379
|
-
allowed_tools: ["read", "search", "apply_patch", "test", "browser", "deploy", "curl"],
|
|
1380
|
-
done_criteria: [
|
|
1381
|
-
"Runtime/preflight blocked without valid scope",
|
|
1382
|
-
"Postflight evidence proves every point",
|
|
1383
|
-
"No old live route, shell, backup, or mount remains reachable",
|
|
1384
|
-
],
|
|
1385
|
-
required_evidence: COMPANY_AGENT_EXECUTION_GOVERNANCE_REQUIRED_EVIDENCE,
|
|
1386
|
-
quality_gates: [COMPANY_AGENT_EXECUTION_GOVERNANCE_GATE_ID, PREMORTEM_GATE_ID],
|
|
1387
|
-
},
|
|
1388
|
-
{
|
|
1389
|
-
template_id: "general_task",
|
|
1390
|
-
title: "General scoped task",
|
|
1391
|
-
description: "Run a normal scoped task with explicit objective, owner, resources, and evidence.",
|
|
1392
|
-
department_name: "general",
|
|
1393
|
-
risk_class: "normal",
|
|
1394
|
-
action_type: "write",
|
|
1395
|
-
allowed_tools: ["read", "search", "apply_patch", "test"],
|
|
1396
|
-
done_criteria: ["Scope stayed within the Work Order", "Changes or findings are summarized", "Evidence covers the requested outcome"],
|
|
1397
|
-
required_evidence: ["diff reviewed or finding report", "verification result"],
|
|
1398
|
-
quality_gates: ["code_change_gate"],
|
|
1399
|
-
},
|
|
1400
|
-
{
|
|
1401
|
-
template_id: "debug_investigation",
|
|
1402
|
-
title: "Debug investigation",
|
|
1403
|
-
description: "Investigate root cause before fixing. No symptom patch without confirmed evidence.",
|
|
1404
|
-
department_name: "engineering",
|
|
1405
|
-
risk_class: "normal",
|
|
1406
|
-
action_type: "code_edit",
|
|
1407
|
-
allowed_tools: ["read", "search", "test", "apply_patch"],
|
|
1408
|
-
done_criteria: ["Root cause is stated", "Reproduction or trace is documented", "Fix addresses the root cause", "Regression or verification check passes"],
|
|
1409
|
-
required_evidence: ["root cause", "reproduction or trace", "regression test or verification command", "fresh verification"],
|
|
1410
|
-
quality_gates: ["debug_gate", "code_change_gate"],
|
|
1411
|
-
},
|
|
1412
|
-
{
|
|
1413
|
-
template_id: "browser_qa",
|
|
1414
|
-
title: "Browser QA",
|
|
1415
|
-
description: "Test a user-facing surface in a real browser with screenshots, console checks, and route coverage.",
|
|
1416
|
-
department_name: "qa",
|
|
1417
|
-
risk_class: "normal",
|
|
1418
|
-
action_type: "browser_qa",
|
|
1419
|
-
allowed_tools: ["browser", "playwright", "read", "screenshot"],
|
|
1420
|
-
done_criteria: ["Core route loads", "Console errors checked", "Interactive flow tested when relevant", "Screenshots or report attached"],
|
|
1421
|
-
required_evidence: ["browser screenshot", "console check", "route or page list", "issue report or pass summary"],
|
|
1422
|
-
quality_gates: ["browser_qa_gate"],
|
|
1423
|
-
},
|
|
1424
|
-
{
|
|
1425
|
-
template_id: "ship_release",
|
|
1426
|
-
title: "Ship or deploy release",
|
|
1427
|
-
description: "Run release work with tests, review, deploy proof, health check, and rollback path.",
|
|
1428
|
-
department_name: "deploy-ops",
|
|
1429
|
-
risk_class: "production",
|
|
1430
|
-
action_type: "deploy",
|
|
1431
|
-
allowed_tools: ["git", "test", "deploy", "curl", "browser"],
|
|
1432
|
-
done_criteria: ["Tests pass", "Diff/release reviewed", "Pre-deploy QA/stale live scan passes", "Deployment health verified", "Deploy manifest and restore point recorded"],
|
|
1433
|
-
required_evidence: DEPLOY_REQUIRED_EVIDENCE,
|
|
1434
|
-
quality_gates: ["release_gate", DEPLOY_PRECHECK_GATE_ID],
|
|
1435
|
-
},
|
|
1436
|
-
{
|
|
1437
|
-
template_id: "design_review",
|
|
1438
|
-
title: "Design review",
|
|
1439
|
-
description: "Review a UI surface for hierarchy, layout, responsive behavior, theme consistency, and content fit.",
|
|
1440
|
-
department_name: "frontend",
|
|
1441
|
-
risk_class: "normal",
|
|
1442
|
-
action_type: "review",
|
|
1443
|
-
allowed_tools: ["browser", "screenshot", "read", "search"],
|
|
1444
|
-
done_criteria: ["Desktop and mobile considered", "Theme and typography checked", "Findings are concrete and scoped"],
|
|
1445
|
-
required_evidence: ["screenshot", "viewport check", "design findings or pass summary"],
|
|
1446
|
-
quality_gates: ["design_review_gate"],
|
|
1447
|
-
},
|
|
1448
|
-
{
|
|
1449
|
-
template_id: "i18n_qa",
|
|
1450
|
-
title: "Language and i18n QA",
|
|
1451
|
-
description: "Verify language separation and translations without mixing locales or bypassing account/browser language rules.",
|
|
1452
|
-
department_name: "translations",
|
|
1453
|
-
risk_class: "normal",
|
|
1454
|
-
action_type: "browser_qa",
|
|
1455
|
-
allowed_tools: ["browser", "search", "read", "test"],
|
|
1456
|
-
done_criteria: ["Each requested locale is checked", "No mixed-language blocks remain", "Language source of truth is respected"],
|
|
1457
|
-
required_evidence: ["locale matrix", "mixed-language scan", "browser or route check"],
|
|
1458
|
-
quality_gates: ["i18n_gate"],
|
|
1459
|
-
},
|
|
1460
|
-
{
|
|
1461
|
-
template_id: "wizard_surface_work",
|
|
1462
|
-
title: "Wizard surface work",
|
|
1463
|
-
description: "Work on a wizard surface only after the target is explicit. builder_v1 and builder_v2 must not be mixed in one task.",
|
|
1464
|
-
department_name: "apps",
|
|
1465
|
-
risk_class: "normal",
|
|
1466
|
-
action_type: "code_edit",
|
|
1467
|
-
allowed_tools: ["read", "search", "apply_patch", "test", "browser"],
|
|
1468
|
-
done_criteria: ["Target explicitly names builder_v1 or builder_v2", "Builder/list/edit routes checked as applicable", "No fallback to the wrong builder", "Language separation checked"],
|
|
1469
|
-
required_evidence: ["explicit wizard target", "builder route check", "browser verification", "language check"],
|
|
1470
|
-
quality_gates: ["wizard_gate", "browser_qa_gate", "i18n_gate"],
|
|
1471
|
-
},
|
|
1472
|
-
{
|
|
1473
|
-
template_id: "context_checkpoint",
|
|
1474
|
-
title: "Context checkpoint",
|
|
1475
|
-
description: "Save decisions, remaining work, affected files, and branch state so another agent can resume safely.",
|
|
1476
|
-
department_name: "general",
|
|
1477
|
-
risk_class: "low",
|
|
1478
|
-
action_type: "read",
|
|
1479
|
-
allowed_tools: ["read", "git", "mem_context_snapshot_create"],
|
|
1480
|
-
done_criteria: ["Snapshot records current state", "Remaining work is concrete", "Uncertainty is named"],
|
|
1481
|
-
required_evidence: ["context snapshot"],
|
|
1482
|
-
quality_gates: ["context_handoff_gate"],
|
|
1483
|
-
},
|
|
1484
|
-
];
|
|
1485
|
-
|
|
1486
|
-
const QUALITY_GATE_TEMPLATES = [
|
|
1487
|
-
{
|
|
1488
|
-
gate_id: COMPANY_AGENT_EXECUTION_GOVERNANCE_GATE_ID,
|
|
1489
|
-
title: "Company agent execution governance v1",
|
|
1490
|
-
required_evidence: COMPANY_AGENT_EXECUTION_GOVERNANCE_REQUIRED_EVIDENCE,
|
|
1491
|
-
minimum_score: 100,
|
|
1492
|
-
version: COMPANY_AGENT_EXECUTION_GOVERNANCE_VERSION,
|
|
1493
|
-
points: COMPANY_AGENT_EXECUTION_GOVERNANCE_POINTS,
|
|
1494
|
-
},
|
|
1495
|
-
{
|
|
1496
|
-
gate_id: PREMORTEM_GATE_ID,
|
|
1497
|
-
title: "Premortem preflight gate",
|
|
1498
|
-
required_evidence: PREMORTEM_REQUIRED_EVIDENCE,
|
|
1499
|
-
minimum_score: 100,
|
|
1500
|
-
version: "2026-06-06",
|
|
1501
|
-
method: "Assume the plan failed six months from now, identify concrete failure reasons, and attach mitigations before build starts.",
|
|
1502
|
-
},
|
|
1503
|
-
{ gate_id: "code_change_gate", title: "Code change gate", required_evidence: ["diff reviewed or finding report", "verification result"], minimum_score: 100 },
|
|
1504
|
-
{ gate_id: "debug_gate", title: "Debug root-cause gate", required_evidence: ["root cause", "reproduction or trace", "fresh verification"], minimum_score: 100 },
|
|
1505
|
-
{ gate_id: "browser_qa_gate", title: "Browser QA gate", required_evidence: ["browser screenshot", "console check", "route or page list"], minimum_score: 100 },
|
|
1506
|
-
{ gate_id: "shared_chrome_gate", title: "Shared Header/Footer gate", required_evidence: ["shared header source", "shared footer source", "desktop header/footer match", "mobile header/footer match", "no shared chrome css leak"], minimum_score: 100 },
|
|
1507
|
-
{ gate_id: "release_gate", title: "Release gate", required_evidence: DEPLOY_REQUIRED_EVIDENCE, minimum_score: 100 },
|
|
1508
|
-
{ gate_id: DEPLOY_PRECHECK_GATE_ID, title: "Deploy precheck gate", required_evidence: DEPLOY_PRECHECK_REQUIRED_EVIDENCE, minimum_score: 100 },
|
|
1509
|
-
{ gate_id: "design_review_gate", title: "Design review gate", required_evidence: ["screenshot", "viewport check", "design findings or pass summary"], minimum_score: 100 },
|
|
1510
|
-
{ gate_id: ANGEL_MOCKUP_GATE_ID, title: "Angel mockup implementation gate", required_evidence: ANGEL_MOCKUP_REQUIRED_EVIDENCE, minimum_score: 100 },
|
|
1511
|
-
{ gate_id: "i18n_gate", title: "i18n gate", required_evidence: ["locale matrix", "mixed-language scan", "browser or route check"], minimum_score: 100 },
|
|
1512
|
-
{ gate_id: "wizard_gate", title: "Wizard target gate", required_evidence: ["explicit wizard target", "builder route check"], minimum_score: 100 },
|
|
1513
|
-
{ gate_id: "context_handoff_gate", title: "Context handoff gate", required_evidence: ["context snapshot"], minimum_score: 100 },
|
|
1514
|
-
];
|
|
1515
|
-
|
|
1516
|
-
function normalizeTemplateId(value) {
|
|
1517
|
-
return String(value || "")
|
|
1518
|
-
.trim()
|
|
1519
|
-
.toLowerCase()
|
|
1520
|
-
.replace(/[^a-z0-9._:-]+/g, "_")
|
|
1521
|
-
.replace(/^_+|_+$/g, "");
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
|
-
function rowToTemplate(row) {
|
|
1525
|
-
return row ? Object.assign({}, row, {
|
|
1526
|
-
allowed_tools: parseJson(row.allowed_tools_json, []),
|
|
1527
|
-
allowed_resources: parseJson(row.allowed_resources_json, []),
|
|
1528
|
-
done_criteria: parseJson(row.done_criteria_json, []),
|
|
1529
|
-
required_evidence: parseJson(row.required_evidence_json, []),
|
|
1530
|
-
quality_gates: parseJson(row.quality_gates_json, []),
|
|
1531
|
-
runtime_contract: parseJson(row.runtime_contract_json, AGENT_NEUTRAL_RUNTIME_CONTRACT),
|
|
1532
|
-
meta: parseJson(row.meta_json, {}),
|
|
1533
|
-
}) : null;
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
|
-
function builtinTemplateRows(scope = DEFAULT_SCOPE) {
|
|
1537
|
-
return BUILTIN_WORK_ORDER_TEMPLATES.map((template) => rowToTemplate({
|
|
1538
|
-
template_id: template.template_id,
|
|
1539
|
-
scope,
|
|
1540
|
-
title: template.title,
|
|
1541
|
-
description: template.description,
|
|
1542
|
-
department_name: template.department_name,
|
|
1543
|
-
risk_class: template.risk_class,
|
|
1544
|
-
action_type: template.action_type,
|
|
1545
|
-
allowed_tools_json: JSON.stringify(template.allowed_tools || []),
|
|
1546
|
-
allowed_resources_json: JSON.stringify(template.allowed_resources || []),
|
|
1547
|
-
done_criteria_json: JSON.stringify(template.done_criteria || []),
|
|
1548
|
-
required_evidence_json: JSON.stringify(template.required_evidence || []),
|
|
1549
|
-
quality_gates_json: JSON.stringify(template.quality_gates || []),
|
|
1550
|
-
runtime_contract_json: JSON.stringify(AGENT_NEUTRAL_RUNTIME_CONTRACT),
|
|
1551
|
-
status: "active",
|
|
1552
|
-
source: "builtin",
|
|
1553
|
-
meta_json: JSON.stringify({ agent_neutral: true }),
|
|
1554
|
-
updated_by: "mnemo",
|
|
1555
|
-
created_at: null,
|
|
1556
|
-
updated_at: null,
|
|
1557
|
-
}));
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
function qualityGateTemplateList(_db, input = {}) {
|
|
1561
|
-
const filter = normalizeTemplateId(input.gate_id || input.id || "");
|
|
1562
|
-
const gates = QUALITY_GATE_TEMPLATES
|
|
1563
|
-
.filter((gate) => !filter || gate.gate_id === filter)
|
|
1564
|
-
.map((gate) => Object.assign({
|
|
1565
|
-
agent_neutral: true,
|
|
1566
|
-
runtime_contract: AGENT_NEUTRAL_RUNTIME_CONTRACT,
|
|
1567
|
-
}, gate));
|
|
1568
|
-
return { ok: true, count: gates.length, gates };
|
|
1569
|
-
}
|
|
1570
|
-
|
|
1571
|
-
function workOrderTemplateList(db, input = {}) {
|
|
1572
|
-
ensureAgentGovernanceSchema(db);
|
|
1573
|
-
const scope = scopeName(input.scope);
|
|
1574
|
-
const idFilter = normalizeTemplateId(input.template_id || input.id || "");
|
|
1575
|
-
const includeInactive = input.include_inactive === true;
|
|
1576
|
-
const byId = new Map();
|
|
1577
|
-
for (const template of builtinTemplateRows(scope)) {
|
|
1578
|
-
if (idFilter && template.template_id !== idFilter) continue;
|
|
1579
|
-
byId.set(template.template_id, template);
|
|
1580
|
-
}
|
|
1581
|
-
const rows = db.prepare("SELECT * FROM work_order_template WHERE scope IN (?, 'default') ORDER BY updated_at DESC").all(scope).map(rowToTemplate);
|
|
1582
|
-
for (const row of rows) {
|
|
1583
|
-
if (!includeInactive && row.status !== "active") continue;
|
|
1584
|
-
if (idFilter && row.template_id !== idFilter) continue;
|
|
1585
|
-
byId.set(row.template_id, row);
|
|
1586
|
-
}
|
|
1587
|
-
const templates = Array.from(byId.values()).sort((a, b) => String(a.template_id).localeCompare(String(b.template_id)));
|
|
1588
|
-
return { ok: true, count: templates.length, templates };
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1591
|
-
function workOrderTemplateUpsert(db, input = {}) {
|
|
1592
|
-
ensureAgentGovernanceSchema(db);
|
|
1593
|
-
const templateId = normalizeTemplateId(input.template_id || input.id);
|
|
1594
|
-
if (!templateId) return { error: "template_id required" };
|
|
1595
|
-
const title = textOrNull(input.title, 240) || templateId;
|
|
1596
|
-
const scope = scopeName(input.scope);
|
|
1597
|
-
const governanceRequired = companyGovernanceRequired(input);
|
|
1598
|
-
const requiredEvidence = withCompanyGovernanceRequiredEvidence(input.required_evidence || input.evidence_required, input);
|
|
1599
|
-
const qualityGates = withCompanyGovernanceQualityGates(input.quality_gates, input);
|
|
1600
|
-
const runtimeContract = Object.assign({}, AGENT_NEUTRAL_RUNTIME_CONTRACT, input.runtime_contract || {}, {
|
|
1601
|
-
company_governance: companyGovernanceMeta(governanceRequired),
|
|
1602
|
-
});
|
|
1603
|
-
const meta = Object.assign({ agent_neutral: true }, input.meta || {}, {
|
|
1604
|
-
company_governance: companyGovernanceMeta(governanceRequired),
|
|
1605
|
-
});
|
|
1606
|
-
db.prepare(`
|
|
1607
|
-
INSERT INTO work_order_template
|
|
1608
|
-
(template_id, scope, title, description, department_name, risk_class, action_type, allowed_tools_json, allowed_resources_json, done_criteria_json, required_evidence_json, quality_gates_json, runtime_contract_json, status, source, meta_json, updated_by, updated_at)
|
|
1609
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
1610
|
-
ON CONFLICT(template_id) DO UPDATE SET
|
|
1611
|
-
scope=excluded.scope,
|
|
1612
|
-
title=excluded.title,
|
|
1613
|
-
description=excluded.description,
|
|
1614
|
-
department_name=excluded.department_name,
|
|
1615
|
-
risk_class=excluded.risk_class,
|
|
1616
|
-
action_type=excluded.action_type,
|
|
1617
|
-
allowed_tools_json=excluded.allowed_tools_json,
|
|
1618
|
-
allowed_resources_json=excluded.allowed_resources_json,
|
|
1619
|
-
done_criteria_json=excluded.done_criteria_json,
|
|
1620
|
-
required_evidence_json=excluded.required_evidence_json,
|
|
1621
|
-
quality_gates_json=excluded.quality_gates_json,
|
|
1622
|
-
runtime_contract_json=excluded.runtime_contract_json,
|
|
1623
|
-
status=excluded.status,
|
|
1624
|
-
source=excluded.source,
|
|
1625
|
-
meta_json=excluded.meta_json,
|
|
1626
|
-
updated_by=excluded.updated_by,
|
|
1627
|
-
updated_at=excluded.updated_at
|
|
1628
|
-
`).run(
|
|
1629
|
-
templateId,
|
|
1630
|
-
scope,
|
|
1631
|
-
title,
|
|
1632
|
-
textOrNull(input.description || input.objective || input.summary, 4000),
|
|
1633
|
-
input.department_name || input.department ? normalizeDepartment(input.department_name || input.department) : null,
|
|
1634
|
-
normalizeRisk(input.risk_class),
|
|
1635
|
-
input.action_type || null,
|
|
1636
|
-
safeJson(normalizeTools(input.allowed_tools || input.tools), []),
|
|
1637
|
-
safeJson(normalizeAllowedResources(input), []),
|
|
1638
|
-
safeJson(listInput(input.done_criteria), []),
|
|
1639
|
-
safeJson(requiredEvidence, []),
|
|
1640
|
-
safeJson(qualityGates, []),
|
|
1641
|
-
safeJson(runtimeContract, AGENT_NEUTRAL_RUNTIME_CONTRACT),
|
|
1642
|
-
input.status || "active",
|
|
1643
|
-
input.source || "custom",
|
|
1644
|
-
safeJson(meta, {}),
|
|
1645
|
-
normalizeAgentName(input.updated_by || input.agent_name || "") || null
|
|
1646
|
-
);
|
|
1647
|
-
return { ok: true, template: workOrderTemplateList(db, { scope, template_id: templateId, include_inactive: true }).templates[0] || null };
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
function getWorkOrderTemplate(db, input = {}) {
|
|
1651
|
-
const templateId = normalizeTemplateId(input.template_id || input.id || input.kind || "general_task");
|
|
1652
|
-
return workOrderTemplateList(db, Object.assign({}, input, { template_id: templateId, include_inactive: false })).templates[0] || null;
|
|
1653
|
-
}
|
|
1654
|
-
|
|
1655
|
-
function workOrderCreateFromTemplate(db, input = {}) {
|
|
1656
|
-
ensureAgentGovernanceSchema(db);
|
|
1657
|
-
const template = getWorkOrderTemplate(db, input);
|
|
1658
|
-
if (!template) return { error: "work_order_template_not_found", template_id: normalizeTemplateId(input.template_id || input.id || input.kind) };
|
|
1659
|
-
const checkInput = Object.assign({}, input, { action_type: input.action_type || template.action_type });
|
|
1660
|
-
const requiredEvidence = withCompanyGovernanceRequiredEvidence([].concat(template.required_evidence || [], listInput(input.required_evidence || input.evidence_required)), checkInput);
|
|
1661
|
-
const doneCriteria = uniqueStrings([].concat(template.done_criteria || [], listInput(input.done_criteria)));
|
|
1662
|
-
const allowedTools = uniqueStrings([].concat(template.allowed_tools || [], normalizeTools(input.allowed_tools || input.tools)));
|
|
1663
|
-
const templateResources = Array.isArray(template.allowed_resources) ? template.allowed_resources : [];
|
|
1664
|
-
const inputResources = normalizeAllowedResources(input);
|
|
1665
|
-
const governanceRequired = companyGovernanceRequired(checkInput);
|
|
1666
|
-
const meta = Object.assign({}, template.meta || {}, input.meta || {}, {
|
|
1667
|
-
template_id: template.template_id,
|
|
1668
|
-
quality_gates: withCompanyGovernanceQualityGates([].concat(template.quality_gates || [], listInput(input.quality_gates)), checkInput),
|
|
1669
|
-
runtime_contract: Object.assign({}, AGENT_NEUTRAL_RUNTIME_CONTRACT, template.runtime_contract || {}, input.runtime_contract || {}, {
|
|
1670
|
-
company_governance: companyGovernanceMeta(governanceRequired),
|
|
1671
|
-
}),
|
|
1672
|
-
company_governance: companyGovernanceMeta(governanceRequired),
|
|
1673
|
-
agent_neutral: true,
|
|
1674
|
-
});
|
|
1675
|
-
return workOrderCreate(db, Object.assign({}, input, {
|
|
1676
|
-
title: input.title || template.title,
|
|
1677
|
-
objective: input.objective || input.summary || input.task || template.description,
|
|
1678
|
-
department_name: input.department_name || input.department || template.department_name,
|
|
1679
|
-
risk_class: input.risk_class || template.risk_class,
|
|
1680
|
-
action_type: input.action_type || template.action_type,
|
|
1681
|
-
allowed_tools: allowedTools,
|
|
1682
|
-
resources: templateResources.concat(inputResources),
|
|
1683
|
-
done_criteria: doneCriteria,
|
|
1684
|
-
required_evidence: requiredEvidence,
|
|
1685
|
-
meta,
|
|
1686
|
-
}));
|
|
1687
|
-
}
|
|
1688
|
-
|
|
1689
|
-
function rowToSnapshot(row) {
|
|
1690
|
-
return row ? Object.assign({}, row, {
|
|
1691
|
-
dirty: !!row.dirty,
|
|
1692
|
-
decisions: parseJson(row.decisions_json, []),
|
|
1693
|
-
remaining_work: parseJson(row.remaining_work_json, []),
|
|
1694
|
-
files: parseJson(row.files_json, []),
|
|
1695
|
-
routes: parseJson(row.routes_json, []),
|
|
1696
|
-
urls: parseJson(row.urls_json, []),
|
|
1697
|
-
meta: parseJson(row.meta_json, {}),
|
|
1698
|
-
}) : null;
|
|
1699
|
-
}
|
|
1700
|
-
|
|
1701
|
-
function contextSnapshotCreate(db, input = {}) {
|
|
1702
|
-
ensureAgentGovernanceSchema(db);
|
|
1703
|
-
const agent = normalizeAgentName(input.agent_name || input.agent);
|
|
1704
|
-
const summary = textOrNull(input.summary || input.context || input.notes, 8000);
|
|
1705
|
-
if (!summary) return { error: "summary required" };
|
|
1706
|
-
const info = db.prepare(`
|
|
1707
|
-
INSERT INTO context_snapshot
|
|
1708
|
-
(scope, project, agent_name, runtime_name, work_order_id, title, summary, decisions_json, remaining_work_json, files_json, routes_json, urls_json, branch, commit_sha, dirty, source_ref, status, meta_json)
|
|
1709
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
1710
|
-
`).run(
|
|
1711
|
-
scopeName(input.scope),
|
|
1712
|
-
input.project || null,
|
|
1713
|
-
agent || null,
|
|
1714
|
-
input.runtime_name || input.runtime || null,
|
|
1715
|
-
input.work_order_id || null,
|
|
1716
|
-
textOrNull(input.title, 240) || "Context snapshot",
|
|
1717
|
-
summary,
|
|
1718
|
-
safeJson(listInput(input.decisions), []),
|
|
1719
|
-
safeJson(listInput(input.remaining_work || input.remaining || input.next_steps), []),
|
|
1720
|
-
safeJson(listInput(input.files), []),
|
|
1721
|
-
safeJson(listInput(input.routes), []),
|
|
1722
|
-
safeJson(listInput(input.urls), []),
|
|
1723
|
-
input.branch || null,
|
|
1724
|
-
input.commit_sha || input.commit || null,
|
|
1725
|
-
input.dirty ? 1 : 0,
|
|
1726
|
-
input.source_ref || null,
|
|
1727
|
-
input.status || "active",
|
|
1728
|
-
safeJson(Object.assign({ agent_neutral: true }, input.meta || {}), {})
|
|
1729
|
-
);
|
|
1730
|
-
return { ok: true, snapshot: rowToSnapshot(db.prepare("SELECT * FROM context_snapshot WHERE id=?").get(info.lastInsertRowid)) };
|
|
1731
|
-
}
|
|
1732
|
-
|
|
1733
|
-
function formatList(title, values) {
|
|
1734
|
-
const list = Array.isArray(values) ? values.filter(Boolean) : [];
|
|
1735
|
-
if (!list.length) return [`## ${title}`, "- (none recorded)"].join("\n");
|
|
1736
|
-
return [`## ${title}`].concat(list.map((item) => "- " + String(item))).join("\n");
|
|
1737
|
-
}
|
|
1738
|
-
|
|
1739
|
-
function contextRestoreBrief(db, input = {}) {
|
|
1740
|
-
ensureAgentGovernanceSchema(db);
|
|
1741
|
-
const where = ["scope=?"];
|
|
1742
|
-
const params = [scopeName(input.scope)];
|
|
1743
|
-
if (input.id || input.snapshot_id) {
|
|
1744
|
-
where.push("id=?");
|
|
1745
|
-
params.push(parseInt(input.id || input.snapshot_id, 10));
|
|
1746
|
-
} else {
|
|
1747
|
-
if (input.project) { where.push("project=?"); params.push(input.project); }
|
|
1748
|
-
if (input.agent_name || input.agent) { where.push("agent_name=?"); params.push(normalizeAgentName(input.agent_name || input.agent)); }
|
|
1749
|
-
if (input.work_order_id) { where.push("work_order_id=?"); params.push(parseInt(input.work_order_id, 10)); }
|
|
1750
|
-
if (input.status) { where.push("status=?"); params.push(input.status); }
|
|
1751
|
-
}
|
|
1752
|
-
const row = db.prepare(`SELECT * FROM context_snapshot WHERE ${where.join(" AND ")} ORDER BY created_at DESC LIMIT 1`).get(...params);
|
|
1753
|
-
const snapshot = rowToSnapshot(row);
|
|
1754
|
-
if (!snapshot) return { error: "context_snapshot_not_found" };
|
|
1755
|
-
const brief = [
|
|
1756
|
-
"# Mnemo Context Restore Brief",
|
|
1757
|
-
"",
|
|
1758
|
-
"Snapshot: #" + snapshot.id,
|
|
1759
|
-
"Project: " + (snapshot.project || "unspecified"),
|
|
1760
|
-
"Agent: " + (snapshot.agent_name || "unspecified"),
|
|
1761
|
-
"Runtime: " + (snapshot.runtime_name || "unspecified"),
|
|
1762
|
-
"Work Order: " + (snapshot.work_order_id || "none"),
|
|
1763
|
-
"Branch: " + (snapshot.branch || "unknown") + (snapshot.commit_sha ? " @ " + snapshot.commit_sha : "") + (snapshot.dirty ? " (dirty)" : ""),
|
|
1764
|
-
"Saved: " + snapshot.created_at,
|
|
1765
|
-
"",
|
|
1766
|
-
"## Summary",
|
|
1767
|
-
snapshot.summary || "(none)",
|
|
1768
|
-
"",
|
|
1769
|
-
formatList("Decisions", snapshot.decisions),
|
|
1770
|
-
"",
|
|
1771
|
-
formatList("Remaining Work", snapshot.remaining_work),
|
|
1772
|
-
"",
|
|
1773
|
-
formatList("Files", snapshot.files),
|
|
1774
|
-
"",
|
|
1775
|
-
formatList("Routes", snapshot.routes),
|
|
1776
|
-
"",
|
|
1777
|
-
"## Resume Rule",
|
|
1778
|
-
"- Treat this brief as context, not company truth.",
|
|
1779
|
-
"- Check current repo state and Mnemo gates before editing.",
|
|
1780
|
-
"- If target/scope changed, create or update a Work Order before work."
|
|
1781
|
-
].join("\n");
|
|
1782
|
-
return { ok: true, snapshot, brief };
|
|
1783
|
-
}
|
|
1784
|
-
|
|
1785
|
-
function rowToGateRun(row) {
|
|
1786
|
-
return row ? Object.assign({}, row, {
|
|
1787
|
-
missing: parseJson(row.missing_json, []),
|
|
1788
|
-
invalid: parseJson(row.invalid_json, []),
|
|
1789
|
-
evidence: parseJson(row.evidence_json, []),
|
|
1790
|
-
meta: parseJson(row.meta_json, {}),
|
|
1791
|
-
}) : null;
|
|
1792
|
-
}
|
|
1793
|
-
|
|
1794
|
-
function qualityGateRun(db, input = {}) {
|
|
1795
|
-
ensureAgentGovernanceSchema(db);
|
|
1796
|
-
const gateId = normalizeTemplateId(input.gate_id || input.template_id || input.gate || "code_change_gate");
|
|
1797
|
-
const gate = QUALITY_GATE_TEMPLATES.find((item) => item.gate_id === gateId);
|
|
1798
|
-
if (!gate) return { error: "quality_gate_template_not_found", gate_id: gateId };
|
|
1799
|
-
const evidence = Array.isArray(input.evidence) ? input.evidence : [];
|
|
1800
|
-
let workOrder = null;
|
|
1801
|
-
if (input.work_order_id) {
|
|
1802
|
-
workOrder = rowToWorkOrder(db.prepare("SELECT * FROM work_order WHERE id=?").get(parseInt(input.work_order_id, 10)));
|
|
1803
|
-
if (!workOrder) return { error: "work_order_not_found", work_order_id: input.work_order_id };
|
|
1804
|
-
}
|
|
1805
|
-
const requirements = uniqueStrings([]
|
|
1806
|
-
.concat(gate.required_evidence || [])
|
|
1807
|
-
.concat(input.include_work_order_evidence === false ? [] : (workOrder && workOrder.required_evidence || []))
|
|
1808
|
-
.concat(listInput(input.required_evidence)));
|
|
1809
|
-
const invalid = evidence.map(validateEvidenceItem).filter(Boolean).concat(evidence.map(nonPassEvidenceReason).filter(Boolean));
|
|
1810
|
-
const missing = requirements.filter((requirement) => !evidence.some((item) => evidenceMatchesRequirement(item, requirement)));
|
|
1811
|
-
const score = Math.max(0, 100 - invalid.length * 25 - missing.length * 20);
|
|
1812
|
-
const status = invalid.length || missing.length || score < (gate.minimum_score || 100) ? "block" : "pass";
|
|
1813
|
-
let run = null;
|
|
1814
|
-
if (input.persist !== false) {
|
|
1815
|
-
const info = db.prepare(`
|
|
1816
|
-
INSERT INTO quality_gate_run
|
|
1817
|
-
(scope, gate_id, project, work_order_id, agent_name, status, score, missing_json, invalid_json, evidence_json, meta_json)
|
|
1818
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?)
|
|
1819
|
-
`).run(
|
|
1820
|
-
scopeName(input.scope),
|
|
1821
|
-
gateId,
|
|
1822
|
-
input.project || (workOrder && workOrder.project) || null,
|
|
1823
|
-
input.work_order_id || null,
|
|
1824
|
-
normalizeAgentName(input.agent_name || input.agent || "") || null,
|
|
1825
|
-
status,
|
|
1826
|
-
score,
|
|
1827
|
-
safeJson(missing, []),
|
|
1828
|
-
safeJson(invalid, []),
|
|
1829
|
-
safeJson(evidence, []),
|
|
1830
|
-
safeJson(Object.assign({ agent_neutral: true }, input.meta || {}), {})
|
|
1831
|
-
);
|
|
1832
|
-
run = rowToGateRun(db.prepare("SELECT * FROM quality_gate_run WHERE id=?").get(info.lastInsertRowid));
|
|
1833
|
-
}
|
|
1834
|
-
return {
|
|
1835
|
-
ok: status === "pass",
|
|
1836
|
-
status,
|
|
1837
|
-
score,
|
|
1838
|
-
gate,
|
|
1839
|
-
required_evidence: requirements,
|
|
1840
|
-
missing,
|
|
1841
|
-
invalid,
|
|
1842
|
-
run,
|
|
1843
|
-
hint: status === "pass" ? "Quality gate passed." : "Do not mark done. Add passing evidence or use needs_review/blocked.",
|
|
1844
|
-
};
|
|
1845
|
-
}
|
|
1846
|
-
|
|
1847
|
-
function workOrderCreate(db, input = {}) {
|
|
1848
|
-
ensureAgentGovernanceSchema(db);
|
|
1849
|
-
const title = textOrNull(input.title, 240) || textOrNull(input.objective, 120) || "Work order";
|
|
1850
|
-
const objective = textOrNull(input.objective || input.summary || input.task, 8000);
|
|
1851
|
-
if (!objective) return { error: "objective required" };
|
|
1852
|
-
const scope = scopeName(input.scope);
|
|
1853
|
-
const department = input.department_name || input.department ? normalizeDepartment(input.department_name || input.department) : null;
|
|
1854
|
-
const agent = input.assigned_agent || input.agent_name ? normalizeAgentName(input.assigned_agent || input.agent_name) : null;
|
|
1855
|
-
const allowedResources = normalizeAllowedResources(input);
|
|
1856
|
-
const governanceRequired = companyGovernanceRequired(input);
|
|
1857
|
-
const requiredEvidence = withCompanyGovernanceRequiredEvidence(input.required_evidence || input.evidence_required, input);
|
|
1858
|
-
const doneCriteria = uniqueStrings(listInput(input.done_criteria).concat(governanceRequired ? ["company_agent_execution_governance_v1 passed before done"] : []));
|
|
1859
|
-
const meta = Object.assign({}, input.meta || {}, {
|
|
1860
|
-
company_governance: companyGovernanceMeta(governanceRequired),
|
|
1861
|
-
quality_gates: withCompanyGovernanceQualityGates(input.meta && input.meta.quality_gates || input.quality_gates, input),
|
|
1862
|
-
});
|
|
1863
|
-
const approvalIds = listInput(input.approval_ids).map(String);
|
|
1864
|
-
const info = db.prepare(`
|
|
1865
|
-
INSERT INTO work_order
|
|
1866
|
-
(scope, project, title, objective, department_name, owner_agent, assigned_agent, status, risk_class, action_type, allowed_tools_json, allowed_resources_json, done_criteria_json, required_evidence_json, approval_ids_json, deadline_at, source_ref, created_by, meta_json)
|
|
1867
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
1868
|
-
`).run(
|
|
1869
|
-
scope,
|
|
1870
|
-
input.project || null,
|
|
1871
|
-
title,
|
|
1872
|
-
objective,
|
|
1873
|
-
department,
|
|
1874
|
-
normalizeAgentName(input.owner_agent || "") || null,
|
|
1875
|
-
agent,
|
|
1876
|
-
input.status || "open",
|
|
1877
|
-
normalizeRisk(input.risk_class),
|
|
1878
|
-
input.action_type || null,
|
|
1879
|
-
safeJson(normalizeTools(input.allowed_tools || input.tools), []),
|
|
1880
|
-
safeJson(allowedResources, []),
|
|
1881
|
-
safeJson(doneCriteria, []),
|
|
1882
|
-
safeJson(requiredEvidence, []),
|
|
1883
|
-
safeJson(approvalIds, []),
|
|
1884
|
-
input.deadline_at || null,
|
|
1885
|
-
input.source_ref || null,
|
|
1886
|
-
normalizeAgentName(input.created_by || input.owner_agent || input.agent_name || "") || null,
|
|
1887
|
-
safeJson(meta, {})
|
|
1888
|
-
);
|
|
1889
|
-
const workOrderId = info.lastInsertRowid;
|
|
1890
|
-
let token = null;
|
|
1891
|
-
if (input.issue_token !== false && agent) {
|
|
1892
|
-
token = capabilityTokenIssue(db, Object.assign({}, input, {
|
|
1893
|
-
scope,
|
|
1894
|
-
work_order_id: workOrderId,
|
|
1895
|
-
agent_name: agent,
|
|
1896
|
-
department_name: department,
|
|
1897
|
-
allowed_resources: { resources: allowedResources },
|
|
1898
|
-
required_evidence: requiredEvidence,
|
|
1899
|
-
approval_ids: approvalIds,
|
|
1900
|
-
reason: input.reason || "issued from work order #" + workOrderId,
|
|
1901
|
-
granted_by: input.created_by || input.owner_agent || input.agent_name,
|
|
1902
|
-
}));
|
|
1903
|
-
if (token && token.token_id) {
|
|
1904
|
-
db.prepare("UPDATE work_order SET token_id=?, status=CASE WHEN status='open' THEN 'issued' ELSE status END, updated_at=strftime('%Y-%m-%dT%H:%M:%fZ','now') WHERE id=?").run(token.token_id, workOrderId);
|
|
1905
|
-
}
|
|
1906
|
-
}
|
|
1907
|
-
return { ok: true, work_order: rowToWorkOrder(db.prepare("SELECT * FROM work_order WHERE id=?").get(workOrderId)), token };
|
|
1908
|
-
}
|
|
1909
|
-
|
|
1910
|
-
function workOrderList(db, input = {}) {
|
|
1911
|
-
ensureAgentGovernanceSchema(db);
|
|
1912
|
-
const where = ["scope=?"];
|
|
1913
|
-
const params = [scopeName(input.scope)];
|
|
1914
|
-
if (input.project) { where.push("project=?"); params.push(input.project); }
|
|
1915
|
-
if (input.assigned_agent || input.agent_name) { where.push("assigned_agent=?"); params.push(normalizeAgentName(input.assigned_agent || input.agent_name)); }
|
|
1916
|
-
if (input.owner_agent) { where.push("owner_agent=?"); params.push(normalizeAgentName(input.owner_agent)); }
|
|
1917
|
-
if (input.department_name || input.department) { where.push("department_name=?"); params.push(normalizeDepartment(input.department_name || input.department)); }
|
|
1918
|
-
if (input.status) { where.push("status=?"); params.push(input.status); }
|
|
1919
|
-
else if (!input.include_done) where.push("status NOT IN ('done','cancelled')");
|
|
1920
|
-
params.push(clampInt(input.limit, 100, 1, 500));
|
|
1921
|
-
const rows = db.prepare(`SELECT * FROM work_order WHERE ${where.join(" AND ")} ORDER BY updated_at DESC, created_at DESC LIMIT ?`).all(...params).map(rowToWorkOrder);
|
|
1922
|
-
return { ok: true, count: rows.length, work_orders: rows };
|
|
1923
|
-
}
|
|
1924
|
-
|
|
1925
|
-
function normalizeEvidenceText(value) {
|
|
1926
|
-
return String(value || "")
|
|
1927
|
-
.toLowerCase()
|
|
1928
|
-
.replace(/[^a-z0-9]+/g, " ")
|
|
1929
|
-
.replace(/\s+/g, " ")
|
|
1930
|
-
.trim();
|
|
1931
|
-
}
|
|
1932
|
-
|
|
1933
|
-
function evidenceText(item) {
|
|
1934
|
-
if (!item || typeof item !== "object") return "";
|
|
1935
|
-
const parts = [
|
|
1936
|
-
item.check,
|
|
1937
|
-
item.name,
|
|
1938
|
-
item.label,
|
|
1939
|
-
item.test_step,
|
|
1940
|
-
item.command,
|
|
1941
|
-
item.result,
|
|
1942
|
-
item.status,
|
|
1943
|
-
item.summary,
|
|
1944
|
-
item.file_path,
|
|
1945
|
-
item.url,
|
|
1946
|
-
item.output_ref,
|
|
1947
|
-
item.receipt_id,
|
|
1948
|
-
];
|
|
1949
|
-
for (const field of ["files", "urls", "artifacts", "screenshots", "required_evidence"]) {
|
|
1950
|
-
if (Array.isArray(item[field])) parts.push(...item[field]);
|
|
1951
|
-
}
|
|
1952
|
-
return normalizeEvidenceText(parts.filter(Boolean).join(" "));
|
|
1953
|
-
}
|
|
1954
|
-
|
|
1955
|
-
function evidenceMatchesRequirement(item, requirement) {
|
|
1956
|
-
const req = normalizeEvidenceText(requirement);
|
|
1957
|
-
if (!req) return true;
|
|
1958
|
-
const text = evidenceText(item);
|
|
1959
|
-
if (!text) return false;
|
|
1960
|
-
if (text.includes(req)) return true;
|
|
1961
|
-
const tokens = req.split(" ").filter((token) => token.length > 2);
|
|
1962
|
-
return tokens.length > 0 && tokens.every((token) => text.includes(token));
|
|
1963
|
-
}
|
|
1964
|
-
|
|
1965
|
-
function evidenceListFromInput(input = {}, fields = []) {
|
|
1966
|
-
const out = [];
|
|
1967
|
-
const keys = fields.length ? fields : ["evidence", "predeploy_evidence", "pre_deploy_evidence", "deploy_precheck_evidence", "deploy_evidence"];
|
|
1968
|
-
for (const key of keys) {
|
|
1969
|
-
const value = input[key];
|
|
1970
|
-
if (Array.isArray(value)) out.push(...value);
|
|
1971
|
-
}
|
|
1972
|
-
const meta = input.meta && typeof input.meta === "object" ? input.meta : {};
|
|
1973
|
-
for (const key of keys) {
|
|
1974
|
-
const value = meta[key];
|
|
1975
|
-
if (Array.isArray(value)) out.push(...value);
|
|
1976
|
-
}
|
|
1977
|
-
return out.filter((item) => item && typeof item === "object" && !Array.isArray(item));
|
|
1978
|
-
}
|
|
1979
|
-
|
|
1980
|
-
function deployPrecheckInputGaps(input = {}) {
|
|
1981
|
-
if (!isDeployAction(input)) return [];
|
|
1982
|
-
const evidence = evidenceListFromInput(input);
|
|
1983
|
-
return DEPLOY_PRECHECK_REQUIRED_EVIDENCE.filter((requirement) => !evidence.some((item) => evidenceMatchesRequirement(item, requirement)));
|
|
1984
|
-
}
|
|
1985
|
-
|
|
1986
|
-
function validateEvidenceItem(item, index) {
|
|
1987
|
-
if (!item || typeof item !== "object" || Array.isArray(item)) {
|
|
1988
|
-
return "evidence[" + index + "] must be an object";
|
|
1989
|
-
}
|
|
1990
|
-
const hasOutcome = item.result != null || item.status != null || item.exit_code != null || item.exitCode != null;
|
|
1991
|
-
const hasCheck = !!textOrNull(item.check || item.name || item.label || item.test_step || item.command, 2000);
|
|
1992
|
-
const hasTarget = !!textOrNull(item.file_path || item.url || item.output_ref || item.receipt_id || item.screenshot_path || item.media_id, 2000)
|
|
1993
|
-
|| ["files", "urls", "artifacts", "screenshots"].some((field) => Array.isArray(item[field]) && item[field].length > 0);
|
|
1994
|
-
if (!hasOutcome) {
|
|
1995
|
-
return "evidence[" + index + "] needs result/status/exit_code";
|
|
1996
|
-
}
|
|
1997
|
-
if (!hasCheck && !hasTarget) {
|
|
1998
|
-
return "evidence[" + index + "] needs command/test_step/check or file/url/artifact reference";
|
|
1999
|
-
}
|
|
2000
|
-
if (item.command && item.exit_code == null && item.exitCode == null) {
|
|
2001
|
-
return "evidence[" + index + "] command evidence needs exit_code";
|
|
2002
|
-
}
|
|
2003
|
-
return null;
|
|
2004
|
-
}
|
|
2005
|
-
|
|
2006
|
-
function nonPassEvidenceReason(item, index) {
|
|
2007
|
-
const exitCode = item && (item.exit_code != null ? item.exit_code : item.exitCode);
|
|
2008
|
-
if (exitCode != null) {
|
|
2009
|
-
const n = Number(exitCode);
|
|
2010
|
-
if (!Number.isFinite(n) || n !== 0) {
|
|
2011
|
-
return "evidence[" + index + "] exit_code must be 0 for done";
|
|
2012
|
-
}
|
|
2013
|
-
}
|
|
2014
|
-
const outcome = normalizeEvidenceText([item && item.result, item && item.status].filter(Boolean).join(" "));
|
|
2015
|
-
if (outcome && /\b(fail|failed|failure|error|errored|exception|blocked|block|needs review|needs_review|incomplete|missing|skipped|cancelled|canceled|timeout|timed out|red|not ok|nok)\b/.test(outcome)) {
|
|
2016
|
-
return "evidence[" + index + "] outcome is not passing for done";
|
|
2017
|
-
}
|
|
2018
|
-
return null;
|
|
2019
|
-
}
|
|
2020
|
-
|
|
2021
|
-
function validateWorkOrderCompletionEvidence(order, input = {}, evidence = []) {
|
|
2022
|
-
const status = String(input.status || "done").toLowerCase();
|
|
2023
|
-
const requiredEvidence = requiredEvidenceForOrder(order, input);
|
|
2024
|
-
if (status !== "done") {
|
|
2025
|
-
return { ok: true, status, missing_required: [], invalid: [], required_evidence: requiredEvidence };
|
|
2026
|
-
}
|
|
2027
|
-
if (!evidence.length) {
|
|
2028
|
-
return {
|
|
2029
|
-
ok: false,
|
|
2030
|
-
error: "evidence_required",
|
|
2031
|
-
status,
|
|
2032
|
-
missing_required: requiredEvidence,
|
|
2033
|
-
invalid: [],
|
|
2034
|
-
required_evidence: requiredEvidence,
|
|
2035
|
-
hint: "done requires concrete evidence. Use status needs_review or blocked when verification is not available.",
|
|
2036
|
-
};
|
|
2037
|
-
}
|
|
2038
|
-
const invalid = evidence.map(validateEvidenceItem).filter(Boolean);
|
|
2039
|
-
if (invalid.length) {
|
|
2040
|
-
return {
|
|
2041
|
-
ok: false,
|
|
2042
|
-
error: "evidence_invalid",
|
|
2043
|
-
status,
|
|
2044
|
-
missing_required: [],
|
|
2045
|
-
invalid,
|
|
2046
|
-
required_evidence: requiredEvidence,
|
|
2047
|
-
hint: "Evidence must include concrete checks with result/status/exit_code and relevant command/file/url/artifact references.",
|
|
2048
|
-
};
|
|
2049
|
-
}
|
|
2050
|
-
const failing = evidence.map(nonPassEvidenceReason).filter(Boolean);
|
|
2051
|
-
if (failing.length) {
|
|
2052
|
-
return {
|
|
2053
|
-
ok: false,
|
|
2054
|
-
error: "evidence_not_passing",
|
|
2055
|
-
status,
|
|
2056
|
-
missing_required: [],
|
|
2057
|
-
invalid: failing,
|
|
2058
|
-
required_evidence: requiredEvidence,
|
|
2059
|
-
hint: "done requires passing evidence. Use status needs_review or blocked for failed checks, non-zero exit codes, or incomplete verification.",
|
|
2060
|
-
};
|
|
2061
|
-
}
|
|
2062
|
-
const missing = requiredEvidence.filter((requirement) => !evidence.some((item) => evidenceMatchesRequirement(item, requirement)));
|
|
2063
|
-
if (missing.length) {
|
|
2064
|
-
return {
|
|
2065
|
-
ok: false,
|
|
2066
|
-
error: "evidence_missing_required",
|
|
2067
|
-
status,
|
|
2068
|
-
missing_required: missing,
|
|
2069
|
-
invalid: [],
|
|
2070
|
-
required_evidence: requiredEvidence,
|
|
2071
|
-
hint: "Each required_evidence item must be explicitly covered by at least one evidence object, preferably with check/name/label.",
|
|
2072
|
-
};
|
|
2073
|
-
}
|
|
2074
|
-
return { ok: true, status, missing_required: [], invalid: [], required_evidence: requiredEvidence };
|
|
2075
|
-
}
|
|
2076
|
-
|
|
2077
|
-
function approvalIdList(value) {
|
|
2078
|
-
return listInput(value).map((item) => String(item || "").trim()).filter(Boolean);
|
|
2079
|
-
}
|
|
2080
|
-
|
|
2081
|
-
function approvalRefsCheck(db, input = {}) {
|
|
2082
|
-
const ids = uniqueStrings(approvalIdList(input.approval_ids).concat(approvalIdList(input.approval_id)));
|
|
2083
|
-
const requireApprovals = input.require_approval === true || input.require_approvals === true;
|
|
2084
|
-
const result = {
|
|
2085
|
-
ok: true,
|
|
2086
|
-
ids,
|
|
2087
|
-
approved: [],
|
|
2088
|
-
missing: [],
|
|
2089
|
-
invalid: [],
|
|
2090
|
-
pending: [],
|
|
2091
|
-
expired: [],
|
|
2092
|
-
blockers: [],
|
|
2093
|
-
};
|
|
2094
|
-
if (!ids.length) {
|
|
2095
|
-
if (requireApprovals) {
|
|
2096
|
-
result.ok = false;
|
|
2097
|
-
result.blockers.push("approval required");
|
|
2098
|
-
}
|
|
2099
|
-
return result;
|
|
2100
|
-
}
|
|
2101
|
-
if (!tableExists(db, "approval_request")) {
|
|
2102
|
-
result.ok = false;
|
|
2103
|
-
result.blockers.push("approval_request table missing");
|
|
2104
|
-
return result;
|
|
2105
|
-
}
|
|
2106
|
-
const now = Date.now();
|
|
2107
|
-
for (const id of ids) {
|
|
2108
|
-
const approvalId = parseInt(id, 10);
|
|
2109
|
-
if (!Number.isFinite(approvalId)) {
|
|
2110
|
-
result.invalid.push({ id, reason: "approval id must be numeric" });
|
|
2111
|
-
continue;
|
|
2112
|
-
}
|
|
2113
|
-
const row = db.prepare("SELECT * FROM approval_request WHERE id=?").get(approvalId);
|
|
2114
|
-
if (!row) {
|
|
2115
|
-
result.missing.push(id);
|
|
2116
|
-
continue;
|
|
2117
|
-
}
|
|
2118
|
-
const status = String(row.status || "").toLowerCase();
|
|
2119
|
-
const expiresAt = row.expires_at ? Date.parse(row.expires_at) : null;
|
|
2120
|
-
if (expiresAt && Number.isFinite(expiresAt) && expiresAt < now) {
|
|
2121
|
-
result.expired.push({ id, status, expires_at: row.expires_at });
|
|
2122
|
-
continue;
|
|
2123
|
-
}
|
|
2124
|
-
if (status !== "approved") {
|
|
2125
|
-
result.pending.push({ id, status: status || "unknown" });
|
|
2126
|
-
continue;
|
|
2127
|
-
}
|
|
2128
|
-
result.approved.push({ id, status, decided_by: row.decided_by || null, decided_at: row.decided_at || null });
|
|
2129
|
-
}
|
|
2130
|
-
if (result.invalid.length) result.blockers.push("invalid approval ids: " + result.invalid.map((item) => item.id).join(", "));
|
|
2131
|
-
if (result.missing.length) result.blockers.push("missing approvals: " + result.missing.join(", "));
|
|
2132
|
-
if (result.pending.length) result.blockers.push("approvals not approved: " + result.pending.map((item) => item.id + "=" + item.status).join(", "));
|
|
2133
|
-
if (result.expired.length) result.blockers.push("expired approvals: " + result.expired.map((item) => item.id).join(", "));
|
|
2134
|
-
result.ok = result.blockers.length === 0;
|
|
2135
|
-
return result;
|
|
2136
|
-
}
|
|
2137
|
-
|
|
2138
|
-
function workOrderComplete(db, input = {}) {
|
|
2139
|
-
ensureAgentGovernanceSchema(db);
|
|
2140
|
-
const id = parseInt(input.id || input.work_order_id, 10);
|
|
2141
|
-
if (!id) return { error: "work_order_id required" };
|
|
2142
|
-
const row = db.prepare("SELECT * FROM work_order WHERE id=?").get(id);
|
|
2143
|
-
if (!row) return { error: "work_order_not_found", work_order_id: id };
|
|
2144
|
-
const order = rowToWorkOrder(row);
|
|
2145
|
-
const evidence = Array.isArray(input.evidence) ? input.evidence : [];
|
|
2146
|
-
const evidenceCheck = validateWorkOrderCompletionEvidence(order, input, evidence);
|
|
2147
|
-
if (!evidenceCheck.ok) {
|
|
2148
|
-
return Object.assign({ work_order_id: id }, evidenceCheck);
|
|
2149
|
-
}
|
|
2150
|
-
const status = evidenceCheck.status || "done";
|
|
2151
|
-
const completionGuard = status === "done" ? completionGuardCheck(db, {
|
|
2152
|
-
scope: input.scope || order.scope,
|
|
2153
|
-
project: input.project || order.project,
|
|
2154
|
-
work_order_id: id,
|
|
2155
|
-
summary: input.completion_summary || input.summary || input.result || order.title || order.objective,
|
|
2156
|
-
evidence,
|
|
2157
|
-
tests: Array.isArray(input.tests) ? input.tests : [],
|
|
2158
|
-
handoff_id: input.handoff_id || input.session_handoff_id || null,
|
|
2159
|
-
blockers: input.blockers || input.open_blockers || [],
|
|
2160
|
-
approval_ids: uniqueStrings(
|
|
2161
|
-
approvalIdList(input.approval_ids)
|
|
2162
|
-
.concat(approvalIdList(input.approval_id))
|
|
2163
|
-
.concat((order.approval_ids || []).map(String))
|
|
2164
|
-
),
|
|
2165
|
-
require_evidence: input.require_evidence,
|
|
2166
|
-
require_tests: input.require_tests,
|
|
2167
|
-
require_handoff: input.require_handoff,
|
|
2168
|
-
require_approval: input.require_approval,
|
|
2169
|
-
require_approvals: input.require_approvals,
|
|
2170
|
-
skip_never_again: input.skip_never_again === true,
|
|
2171
|
-
}) : null;
|
|
2172
|
-
if (completionGuard && !completionGuard.ok) {
|
|
2173
|
-
return {
|
|
2174
|
-
work_order_id: id,
|
|
2175
|
-
error: "completion_guard_blocked",
|
|
2176
|
-
completion_guard: completionGuard,
|
|
2177
|
-
hint: "done requires passing evidence, tests/checks, a handoff, approved approval refs when present, and no unfinished blockers.",
|
|
2178
|
-
};
|
|
2179
|
-
}
|
|
2180
|
-
const completedAtSql = status === "done" || status === "cancelled"
|
|
2181
|
-
? "strftime('%Y-%m-%dT%H:%M:%fZ','now')"
|
|
2182
|
-
: "completed_at";
|
|
2183
|
-
db.prepare(`
|
|
2184
|
-
UPDATE work_order
|
|
2185
|
-
SET status=?, completion_summary=?, handoff_id=?, evidence_json=?, updated_at=strftime('%Y-%m-%dT%H:%M:%fZ','now'), completed_at=${completedAtSql}
|
|
2186
|
-
WHERE id=?
|
|
2187
|
-
`).run(
|
|
2188
|
-
status,
|
|
2189
|
-
textOrNull(input.completion_summary || input.summary || input.result, 8000),
|
|
2190
|
-
input.handoff_id || null,
|
|
2191
|
-
safeJson(evidence, []),
|
|
2192
|
-
id
|
|
2193
|
-
);
|
|
2194
|
-
return { ok: true, status, evidence_check: evidenceCheck, completion_guard: completionGuard, work_order: rowToWorkOrder(db.prepare("SELECT * FROM work_order WHERE id=?").get(id)) };
|
|
2195
|
-
}
|
|
2196
|
-
|
|
2197
|
-
function capabilityTokenIssue(db, input = {}) {
|
|
2198
|
-
ensureAgentGovernanceSchema(db);
|
|
2199
|
-
const agent = normalizeAgentName(input.agent_name || input.assigned_agent);
|
|
2200
|
-
if (!agent) return { error: "agent_name required" };
|
|
2201
|
-
const scope = scopeName(input.scope);
|
|
2202
|
-
const workOrder = input.work_order_id ? db.prepare("SELECT * FROM work_order WHERE id=?").get(parseInt(input.work_order_id, 10)) : null;
|
|
2203
|
-
if (input.work_order_id && !workOrder) return { error: "work_order_not_found", work_order_id: input.work_order_id };
|
|
2204
|
-
const wo = rowToWorkOrder(workOrder);
|
|
2205
|
-
const checkInput = Object.assign({}, wo || {}, input, { action_type: input.action_type || (wo && wo.action_type) });
|
|
2206
|
-
const governanceRequired = companyGovernanceRequired(checkInput);
|
|
2207
|
-
const allowedResources = normalizeAllowedResources(input.allowed_resources ? input : (wo ? { allowed_resources: { resources: wo.allowed_resources } } : input));
|
|
2208
|
-
const allowedTools = normalizeTools(input.allowed_tools || input.tools || (wo && wo.allowed_tools));
|
|
2209
|
-
const requiredEvidence = withCompanyGovernanceRequiredEvidence(input.required_evidence || (wo && wo.required_evidence) || input.evidence_required, checkInput);
|
|
2210
|
-
const approvalIds = listInput(input.approval_ids || (wo && wo.approval_ids)).map(String);
|
|
2211
|
-
const tokenId = input.token_id || "cap-" + sha([scope, agent, input.work_order_id || "", nowIso(), Math.random()].join("|")).slice(0, 24);
|
|
2212
|
-
const meta = Object.assign({}, input.meta || {}, {
|
|
2213
|
-
company_governance: companyGovernanceMeta(governanceRequired),
|
|
2214
|
-
});
|
|
2215
|
-
db.prepare(`
|
|
2216
|
-
INSERT INTO capability_token
|
|
2217
|
-
(token_id, scope, work_order_id, agent_name, department_name, project, risk_class, action_type, allowed_tools_json, allowed_resources_json, required_evidence_json, approval_ids_json, budgets_json, status, granted_by, reason, expires_at, meta_json)
|
|
2218
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
2219
|
-
`).run(
|
|
2220
|
-
tokenId,
|
|
2221
|
-
scope,
|
|
2222
|
-
input.work_order_id || null,
|
|
2223
|
-
agent,
|
|
2224
|
-
input.department_name ? normalizeDepartment(input.department_name) : (wo && wo.department_name) || null,
|
|
2225
|
-
input.project || (wo && wo.project) || null,
|
|
2226
|
-
normalizeRisk(input.risk_class || (wo && wo.risk_class)),
|
|
2227
|
-
input.action_type || (wo && wo.action_type) || null,
|
|
2228
|
-
safeJson(allowedTools, []),
|
|
2229
|
-
safeJson(allowedResources, []),
|
|
2230
|
-
safeJson(requiredEvidence, []),
|
|
2231
|
-
safeJson(approvalIds, []),
|
|
2232
|
-
safeJson(input.budgets || {}, {}),
|
|
2233
|
-
input.status || "active",
|
|
2234
|
-
normalizeAgentName(input.granted_by || input.created_by || "") || null,
|
|
2235
|
-
textOrNull(input.reason || "capability token issued", 1200),
|
|
2236
|
-
expiresAt(input),
|
|
2237
|
-
safeJson(meta, {})
|
|
2238
|
-
);
|
|
2239
|
-
return { ok: true, token_id: tokenId, token: rowToToken(db.prepare("SELECT * FROM capability_token WHERE token_id=?").get(tokenId)) };
|
|
2240
|
-
}
|
|
2241
|
-
|
|
2242
|
-
function toolMatches(allowedTools, requestedTool) {
|
|
2243
|
-
if (!requestedTool || !allowedTools.length || allowedTools.includes("*")) return true;
|
|
2244
|
-
const req = String(requestedTool).toLowerCase();
|
|
2245
|
-
return allowedTools.some((tool) => {
|
|
2246
|
-
const allowed = String(tool || "").toLowerCase();
|
|
2247
|
-
if (allowed === req) return true;
|
|
2248
|
-
if (allowed.endsWith("*")) return req.startsWith(allowed.slice(0, -1));
|
|
2249
|
-
return false;
|
|
2250
|
-
});
|
|
2251
|
-
}
|
|
2252
|
-
|
|
2253
|
-
function resourceMatchesOne(allowed, requested) {
|
|
2254
|
-
if (!allowed || !requested) return false;
|
|
2255
|
-
if (allowed.resource_kind === "*" && allowed.resource_key === "*") return true;
|
|
2256
|
-
if (allowed.resource_kind !== requested.resource_kind) return false;
|
|
2257
|
-
const a = String(allowed.resource_key || "");
|
|
2258
|
-
const r = String(requested.resource_key || "");
|
|
2259
|
-
if (a === r) return true;
|
|
2260
|
-
if (a.endsWith("*")) return r.startsWith(a.slice(0, -1));
|
|
2261
|
-
if (allowed.resource_kind === "file" && (a.endsWith("/") || a.endsWith("/*"))) {
|
|
2262
|
-
const prefix = a.replace(/\*$/, "");
|
|
2263
|
-
return r.startsWith(prefix);
|
|
2264
|
-
}
|
|
2265
|
-
return false;
|
|
2266
|
-
}
|
|
2267
|
-
|
|
2268
|
-
function matchRequestedResources(allowedResources, requested) {
|
|
2269
|
-
if (!requested.length) return { ok: true, matched: [], missing: [] };
|
|
2270
|
-
if (!allowedResources.length) return { ok: false, matched: [], missing: requested };
|
|
2271
|
-
const matched = [];
|
|
2272
|
-
const missing = [];
|
|
2273
|
-
for (const req of requested) {
|
|
2274
|
-
const hit = allowedResources.find((allowed) => resourceMatchesOne(allowed, req));
|
|
2275
|
-
if (hit) matched.push({ requested: req, allowed: hit });
|
|
2276
|
-
else missing.push(req);
|
|
2277
|
-
}
|
|
2278
|
-
return { ok: missing.length === 0, matched, missing };
|
|
2279
|
-
}
|
|
2280
|
-
|
|
2281
|
-
function isCriticalRisk(risk) {
|
|
2282
|
-
return /^(critical|live-risk|production|billing-risk|auth-risk|deploy|billing|auth)$/.test(normalizeRisk(risk));
|
|
2283
|
-
}
|
|
2284
|
-
|
|
2285
|
-
function requiresCapabilityToken(input = {}) {
|
|
2286
|
-
const action = String(input.action_type || "").toLowerCase();
|
|
2287
|
-
const text = [
|
|
2288
|
-
input.task,
|
|
2289
|
-
input.summary,
|
|
2290
|
-
input.tool_name,
|
|
2291
|
-
action,
|
|
2292
|
-
Array.isArray(input.files) ? input.files.join(" ") : "",
|
|
2293
|
-
Array.isArray(input.routes) ? input.routes.join(" ") : "",
|
|
2294
|
-
Array.isArray(input.domains) ? input.domains.join(" ") : "",
|
|
2295
|
-
Array.isArray(input.system_names) ? input.system_names.join(" ") : "",
|
|
2296
|
-
].filter(Boolean).join(" ").toLowerCase();
|
|
2297
|
-
if (RISKY_ACTIONS.has(action)) return true;
|
|
2298
|
-
if (/\b(edit|write|patch|delete|remove|move|rename|deploy|restart|pm2|nginx|dns|migrate|migration|stripe|billing|vat|auth|login|oauth|session|production|live)\b/.test(text)) return true;
|
|
2299
|
-
if ((Array.isArray(input.files) && input.files.length) && action !== "read") return true;
|
|
2300
|
-
if ((Array.isArray(input.routes) && input.routes.length) && action !== "read") return true;
|
|
2301
|
-
if ((Array.isArray(input.domains) && input.domains.length) && action !== "read") return true;
|
|
2302
|
-
if ((Array.isArray(input.system_names) && input.system_names.length) && action !== "read") return true;
|
|
2303
|
-
if (Array.isArray(input.resources) && input.resources.length && action !== "read") return true;
|
|
2304
|
-
return false;
|
|
2305
|
-
}
|
|
2306
|
-
|
|
2307
|
-
function auditTokenCheck(db, token, input, result) {
|
|
2308
|
-
ensureAgentGovernanceSchema(db);
|
|
2309
|
-
const info = db.prepare(`
|
|
2310
|
-
INSERT INTO capability_token_audit
|
|
2311
|
-
(token_id, work_order_id, agent_name, project, event_kind, granted, reason, matched_scope_json, missing_approval, required_evidence_json, action_payload_json)
|
|
2312
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?)
|
|
2313
|
-
`).run(
|
|
2314
|
-
token && token.token_id || input.token_id || input.capability_token_id || null,
|
|
2315
|
-
token && token.work_order_id || input.work_order_id || null,
|
|
2316
|
-
normalizeAgentName(input.agent_name || token && token.agent_name || ""),
|
|
2317
|
-
input.project || token && token.project || null,
|
|
2318
|
-
input.event_kind || "check",
|
|
2319
|
-
result.granted ? 1 : 0,
|
|
2320
|
-
result.reason || null,
|
|
2321
|
-
safeJson(result.matched_scope || {}, {}),
|
|
2322
|
-
result.missing_approval ? 1 : 0,
|
|
2323
|
-
safeJson(result.required_evidence || [], []),
|
|
2324
|
-
safeJson(input, {})
|
|
2325
|
-
);
|
|
2326
|
-
return info.lastInsertRowid;
|
|
2327
|
-
}
|
|
2328
|
-
|
|
2329
|
-
function capabilityTokenCheck(db, input = {}) {
|
|
2330
|
-
ensureAgentGovernanceSchema(db);
|
|
2331
|
-
let tokenId = String(input.token_id || input.capability_token_id || input.capability_token || "").trim();
|
|
2332
|
-
const required = requiresCapabilityToken(input);
|
|
2333
|
-
if (required && !tokenId && input.work_order_id) {
|
|
2334
|
-
const workOrderId = parseInt(input.work_order_id, 10);
|
|
2335
|
-
if (workOrderId) {
|
|
2336
|
-
const workOrderToken = db.prepare("SELECT token_id FROM work_order WHERE id=?").get(workOrderId);
|
|
2337
|
-
if (workOrderToken && workOrderToken.token_id) tokenId = String(workOrderToken.token_id).trim();
|
|
2338
|
-
}
|
|
2339
|
-
}
|
|
2340
|
-
if (!required && !tokenId) {
|
|
2341
|
-
const result = { ok: true, granted: true, reason: "capability token not required for low-risk/read action", required: false, matched_scope: {}, missing_approval: false, required_evidence: [], expires_at: null };
|
|
2342
|
-
result.audit_id = auditTokenCheck(db, null, input, result);
|
|
2343
|
-
return result;
|
|
2344
|
-
}
|
|
2345
|
-
if (!tokenId) {
|
|
2346
|
-
const result = { ok: false, granted: false, reason: "capability_token_required", required: true, matched_scope: {}, missing_approval: false, required_evidence: [], expires_at: null };
|
|
2347
|
-
result.audit_id = auditTokenCheck(db, null, input, result);
|
|
2348
|
-
return result;
|
|
2349
|
-
}
|
|
2350
|
-
const token = rowToToken(db.prepare("SELECT * FROM capability_token WHERE token_id=?").get(tokenId));
|
|
2351
|
-
if (!token) {
|
|
2352
|
-
const result = { ok: false, granted: false, reason: "capability_token_not_found", required: true, matched_scope: {}, missing_approval: false, required_evidence: [], expires_at: null };
|
|
2353
|
-
result.audit_id = auditTokenCheck(db, null, input, result);
|
|
2354
|
-
return result;
|
|
2355
|
-
}
|
|
2356
|
-
const blockers = [];
|
|
2357
|
-
const agent = normalizeAgentName(input.agent_name);
|
|
2358
|
-
if (token.status !== "active") blockers.push("token status is " + token.status);
|
|
2359
|
-
if (Date.parse(token.expires_at) < Date.now()) blockers.push("token expired");
|
|
2360
|
-
if (agent && token.agent_name !== agent) blockers.push("token belongs to " + token.agent_name);
|
|
2361
|
-
if (input.work_order_id && token.work_order_id && Number(input.work_order_id) !== Number(token.work_order_id)) blockers.push("token belongs to work_order #" + token.work_order_id);
|
|
2362
|
-
if (input.project && token.project && input.project !== token.project) blockers.push("token project mismatch");
|
|
2363
|
-
if (token.action_type && input.action_type && String(token.action_type).toLowerCase() !== String(input.action_type).toLowerCase()) blockers.push("token action_type mismatch");
|
|
2364
|
-
if (!toolMatches(token.allowed_tools || [], input.tool_name)) blockers.push("tool not covered by token");
|
|
2365
|
-
const governanceInput = Object.assign({}, input, { action_type: input.action_type || token.action_type });
|
|
2366
|
-
const governanceRequired = companyGovernanceRequired(governanceInput);
|
|
2367
|
-
const governanceGaps = governanceRequired ? companyGovernanceRequirementGaps(token.required_evidence || []) : [];
|
|
2368
|
-
if (governanceGaps.length) {
|
|
2369
|
-
blockers.push("company governance gate missing required evidence: " + governanceGaps.join(", "));
|
|
2370
|
-
}
|
|
2371
|
-
const deployGaps = deployRequirementGaps(token.required_evidence || [], governanceInput);
|
|
2372
|
-
if (deployGaps.length) {
|
|
2373
|
-
blockers.push("deploy gate missing required evidence: " + deployGaps.join(", "));
|
|
2374
|
-
}
|
|
2375
|
-
const requested = requestedResources(input);
|
|
2376
|
-
const resourceMatch = matchRequestedResources(token.allowed_resources || [], requested);
|
|
2377
|
-
if (!resourceMatch.ok) blockers.push("requested resources not covered by token");
|
|
2378
|
-
const approvals = uniqueStrings(approvalIdList(input.approval_ids).concat(approvalIdList(input.approval_id)).concat((token.approval_ids || []).map(String)));
|
|
2379
|
-
const approvalNotRequired = boolFlag(token.meta && token.meta.approval_not_required, false);
|
|
2380
|
-
const approvalRequired = input.require_approval === true || boolFlag(token.meta && token.meta.requires_approval, false) || isCriticalRisk(token.risk_class);
|
|
2381
|
-
const approvalCheck = (approvals.length || (approvalRequired && !approvalNotRequired))
|
|
2382
|
-
? approvalRefsCheck(db, { approval_ids: approvals, require_approval: approvalRequired && !approvalNotRequired })
|
|
2383
|
-
: { ok: true, ids: [], approved: [], missing: [], invalid: [], pending: [], expired: [], blockers: [] };
|
|
2384
|
-
if (!approvalCheck.ok) blockers.push(...approvalCheck.blockers.map((entry) => "approval " + entry));
|
|
2385
|
-
const result = {
|
|
2386
|
-
ok: blockers.length === 0,
|
|
2387
|
-
granted: blockers.length === 0,
|
|
2388
|
-
reason: blockers.length ? blockers.join("; ") : "capability token grants this action",
|
|
2389
|
-
required,
|
|
2390
|
-
token_id: token.token_id,
|
|
2391
|
-
work_order_id: token.work_order_id,
|
|
2392
|
-
agent_name: token.agent_name,
|
|
2393
|
-
project: token.project,
|
|
2394
|
-
matched_scope: { resources: resourceMatch.matched, missing_resources: resourceMatch.missing, tool_name: input.tool_name || null },
|
|
2395
|
-
missing_approval: !approvalCheck.ok,
|
|
2396
|
-
approval_check: approvalCheck,
|
|
2397
|
-
required_evidence: token.required_evidence || [],
|
|
2398
|
-
company_governance: {
|
|
2399
|
-
required: governanceRequired,
|
|
2400
|
-
gate_id: COMPANY_AGENT_EXECUTION_GOVERNANCE_GATE_ID,
|
|
2401
|
-
version: COMPANY_AGENT_EXECUTION_GOVERNANCE_VERSION,
|
|
2402
|
-
missing_required_evidence: governanceGaps,
|
|
2403
|
-
},
|
|
2404
|
-
deploy_gate: {
|
|
2405
|
-
required: isDeployAction(governanceInput),
|
|
2406
|
-
gate_id: DEPLOY_PRECHECK_GATE_ID,
|
|
2407
|
-
missing_required_evidence: deployGaps,
|
|
2408
|
-
},
|
|
2409
|
-
expires_at: token.expires_at,
|
|
2410
|
-
};
|
|
2411
|
-
result.audit_id = auditTokenCheck(db, token, input, result);
|
|
2412
|
-
return result;
|
|
2413
|
-
}
|
|
2414
|
-
|
|
2415
|
-
function capabilityTokenRevoke(db, input = {}) {
|
|
2416
|
-
ensureAgentGovernanceSchema(db);
|
|
2417
|
-
const tokenId = String(input.token_id || input.capability_token_id || "").trim();
|
|
2418
|
-
const by = normalizeAgentName(input.revoked_by || input.agent_name || "");
|
|
2419
|
-
if (!tokenId || !by) return { error: "token_id + revoked_by required" };
|
|
2420
|
-
const info = db.prepare("UPDATE capability_token SET status='revoked', revoked_at=strftime('%Y-%m-%dT%H:%M:%fZ','now'), meta_json=COALESCE(?, meta_json) WHERE token_id=? AND status='active'")
|
|
2421
|
-
.run(input.meta ? safeJson(input.meta, {}) : null, tokenId);
|
|
2422
|
-
const result = { granted: false, reason: input.reason || "token revoked", matched_scope: {}, missing_approval: false, required_evidence: [] };
|
|
2423
|
-
const auditId = auditTokenCheck(db, { token_id: tokenId }, { token_id: tokenId, agent_name: by, event_kind: "revoke" }, result);
|
|
2424
|
-
return { ok: info.changes > 0, token_id: tokenId, revoked_by: by, audit_id: auditId };
|
|
2425
|
-
}
|
|
2426
|
-
|
|
2427
|
-
function resourceOwner(db, input = {}) {
|
|
2428
|
-
if (!tableExists(db, "org_resource")) return null;
|
|
2429
|
-
const kind = normalizeResourceKind(input.resource_kind);
|
|
2430
|
-
const key = normalizeResourceKey(kind, input.resource_key || input.file_path || input.route || input.domain || input.system_name);
|
|
2431
|
-
if (!kind || !key) return null;
|
|
2432
|
-
try {
|
|
2433
|
-
return db.prepare("SELECT * FROM org_resource WHERE scope=? AND resource_kind=? AND resource_key=? AND status='active' ORDER BY updated_at DESC LIMIT 1")
|
|
2434
|
-
.get(scopeName(input.scope), kind, key) || null;
|
|
2435
|
-
} catch {
|
|
2436
|
-
return null;
|
|
2437
|
-
}
|
|
2438
|
-
}
|
|
2439
|
-
|
|
2440
|
-
function departmentLead(db, scope, department) {
|
|
2441
|
-
const charter = departmentCharterGet(db, { scope, department_name: department }).charter;
|
|
2442
|
-
if (charter && (charter.lead_agent || charter.review_agent)) return charter.lead_agent || charter.review_agent;
|
|
2443
|
-
if (tableExists(db, "department")) {
|
|
2444
|
-
try {
|
|
2445
|
-
const row = db.prepare("SELECT lead_agent, review_agent FROM department WHERE name=?").get(department);
|
|
2446
|
-
if (row) return row.lead_agent || row.review_agent || null;
|
|
2447
|
-
} catch {}
|
|
2448
|
-
}
|
|
2449
|
-
return null;
|
|
2450
|
-
}
|
|
2451
|
-
|
|
2452
|
-
function intentRoute(db, input = {}) {
|
|
2453
|
-
ensureAgentGovernanceSchema(db);
|
|
2454
|
-
const scope = scopeName(input.scope);
|
|
2455
|
-
const intentKind = String(input.intent_kind || input.intent || "request").toLowerCase().replace(/[^a-z0-9_:-]+/g, "_");
|
|
2456
|
-
const department = input.department_name || input.department ? normalizeDepartment(input.department_name || input.department) : null;
|
|
2457
|
-
const owner = resourceOwner(db, input);
|
|
2458
|
-
const targetDepartment = department || (owner && owner.owning_department) || null;
|
|
2459
|
-
const routeToAgent = normalizeAgentName(input.route_to_agent || (owner && owner.owner_agent) || (targetDepartment && departmentLead(db, scope, targetDepartment)) || process.env.MNEMO_DEFAULT_COORDINATOR || "agent-a");
|
|
2460
|
-
const routeToDepartment = input.route_to_department || targetDepartment || null;
|
|
2461
|
-
const reason = input.reason || (owner ? `resource owner ${owner.owner_agent || "unknown"}` : (targetDepartment ? `department lead for ${targetDepartment}` : "default coordinator"));
|
|
2462
|
-
let briefId = null;
|
|
2463
|
-
if (input.write_brief === true && tableExists(db, "agent_brief")) {
|
|
2464
|
-
const content = [
|
|
2465
|
-
"# Intent Route",
|
|
2466
|
-
"",
|
|
2467
|
-
"## Intent",
|
|
2468
|
-
intentKind,
|
|
2469
|
-
"",
|
|
2470
|
-
"## Request",
|
|
2471
|
-
input.summary || input.request || input.reason || "",
|
|
2472
|
-
"",
|
|
2473
|
-
"## Project",
|
|
2474
|
-
input.project || "unspecified",
|
|
2475
|
-
"",
|
|
2476
|
-
"## Resource",
|
|
2477
|
-
[input.resource_kind, input.resource_key || input.file_path || input.route || input.domain || input.system_name].filter(Boolean).join(":") || "unspecified",
|
|
2478
|
-
"",
|
|
2479
|
-
"## Report Back",
|
|
2480
|
-
"- decision or route result",
|
|
2481
|
-
"- approval/denial if access-related",
|
|
2482
|
-
].join("\n");
|
|
2483
|
-
const info = db.prepare("INSERT INTO agent_brief (agent_name, source_agent, content, meta_json) VALUES (?,?,?,?)")
|
|
2484
|
-
.run(routeToAgent, normalizeAgentName(input.agent_name || "intent-router"), content, safeJson({ intent_kind: intentKind, project: input.project || null }, {}));
|
|
2485
|
-
briefId = info.lastInsertRowid;
|
|
2486
|
-
}
|
|
2487
|
-
const info = db.prepare(`
|
|
2488
|
-
INSERT INTO intent_route
|
|
2489
|
-
(scope, intent_kind, agent_name, project, department_name, resource_kind, resource_key, summary, route_to_agent, route_to_department, reason, status, brief_id, meta_json)
|
|
2490
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
2491
|
-
`).run(
|
|
2492
|
-
scope,
|
|
2493
|
-
intentKind,
|
|
2494
|
-
normalizeAgentName(input.agent_name || "") || null,
|
|
2495
|
-
input.project || null,
|
|
2496
|
-
targetDepartment,
|
|
2497
|
-
input.resource_kind ? normalizeResourceKind(input.resource_kind) : null,
|
|
2498
|
-
input.resource_key ? normalizeResourceKey(input.resource_kind, input.resource_key) : null,
|
|
2499
|
-
textOrNull(input.summary || input.request || input.reason, 1200),
|
|
2500
|
-
routeToAgent,
|
|
2501
|
-
routeToDepartment,
|
|
2502
|
-
reason,
|
|
2503
|
-
input.status || "routed",
|
|
2504
|
-
briefId,
|
|
2505
|
-
safeJson(input.meta || {}, {})
|
|
2506
|
-
);
|
|
2507
|
-
return { ok: true, id: info.lastInsertRowid, intent_kind: intentKind, route_to_agent: routeToAgent, route_to_department: routeToDepartment, reason, brief_id: briefId };
|
|
2508
|
-
}
|
|
2509
|
-
|
|
2510
|
-
function countRows(db, sql, params = []) {
|
|
2511
|
-
try { return db.prepare(sql).get(...params).c || 0; } catch { return 0; }
|
|
2512
|
-
}
|
|
2513
|
-
|
|
2514
|
-
function autonomyLevel(score) {
|
|
2515
|
-
if (score < 50) return "L0";
|
|
2516
|
-
if (score < 65) return "L1";
|
|
2517
|
-
if (score < 80) return "L2";
|
|
2518
|
-
if (score < 90) return "L3";
|
|
2519
|
-
return "L4";
|
|
2520
|
-
}
|
|
2521
|
-
|
|
2522
|
-
function autonomyMeaning(level) {
|
|
2523
|
-
return {
|
|
2524
|
-
L0: "read-only",
|
|
2525
|
-
L1: "proposals and reports",
|
|
2526
|
-
L2: "own low-risk files with token",
|
|
2527
|
-
L3: "tests/builds and normal work orders with token",
|
|
2528
|
-
L4: "limited low-risk deployments with token and evidence",
|
|
2529
|
-
L5: "critical decisions only with explicit approval; never automatic",
|
|
2530
|
-
}[level] || "unknown";
|
|
2531
|
-
}
|
|
2532
|
-
|
|
2533
|
-
function autonomyScoreReport(db, input = {}) {
|
|
2534
|
-
ensureAgentGovernanceSchema(db);
|
|
2535
|
-
const agent = normalizeAgentName(input.agent_name);
|
|
2536
|
-
if (!agent) return { error: "agent_name required" };
|
|
2537
|
-
const days = clampInt(input.window_days || input.days, 7, 1, 90);
|
|
2538
|
-
const since = new Date(Date.now() - days * 86400000).toISOString();
|
|
2539
|
-
const actions = tableExists(db, "agent_action") ? {
|
|
2540
|
-
done: countRows(db, "SELECT COUNT(*) c FROM agent_action WHERE agent_name=? AND status IN ('done','ok','completed') AND started_at>=?", [agent, since]),
|
|
2541
|
-
failed: countRows(db, "SELECT COUNT(*) c FROM agent_action WHERE agent_name=? AND status IN ('failed','error','auth_failed') AND started_at>=?", [agent, since]),
|
|
2542
|
-
blocked: countRows(db, "SELECT COUNT(*) c FROM agent_action WHERE agent_name=? AND status IN ('blocked','block') AND started_at>=?", [agent, since]),
|
|
2543
|
-
guard_blocked: countRows(db, "SELECT COUNT(*) c FROM agent_action WHERE agent_name=? AND status LIKE '%guard%' AND started_at>=?", [agent, since]),
|
|
2544
|
-
} : { done: 0, failed: 0, blocked: 0, guard_blocked: 0 };
|
|
2545
|
-
const briefs = tableExists(db, "agent_brief") ? {
|
|
2546
|
-
pending: countRows(db, "SELECT COUNT(*) c FROM agent_brief WHERE agent_name=? AND status IN ('pending','dispatched')", [agent]),
|
|
2547
|
-
done: countRows(db, "SELECT COUNT(*) c FROM agent_brief WHERE agent_name=? AND status='done' AND COALESCE(done_at, created_at)>=?", [agent, since]),
|
|
2548
|
-
} : { pending: 0, done: 0 };
|
|
2549
|
-
const findings = tableExists(db, "quality_finding") ? {
|
|
2550
|
-
open_high: countRows(db, "SELECT COUNT(*) c FROM quality_finding WHERE source_agent=? AND status='open' AND severity IN ('H','critical')", [agent]),
|
|
2551
|
-
open: countRows(db, "SELECT COUNT(*) c FROM quality_finding WHERE source_agent=? AND status='open'", [agent]),
|
|
2552
|
-
} : { open_high: 0, open: 0 };
|
|
2553
|
-
const tokenAudits = tableExists(db, "capability_token_audit") ? {
|
|
2554
|
-
granted: countRows(db, "SELECT COUNT(*) c FROM capability_token_audit WHERE agent_name=? AND granted=1 AND created_at>=?", [agent, since]),
|
|
2555
|
-
denied: countRows(db, "SELECT COUNT(*) c FROM capability_token_audit WHERE agent_name=? AND granted=0 AND created_at>=?", [agent, since]),
|
|
2556
|
-
} : { granted: 0, denied: 0 };
|
|
2557
|
-
let score = 100;
|
|
2558
|
-
score -= actions.failed * 5;
|
|
2559
|
-
score -= actions.blocked * 4;
|
|
2560
|
-
score -= actions.guard_blocked * 8;
|
|
2561
|
-
score -= Math.max(0, briefs.pending - 3) * 2;
|
|
2562
|
-
score -= findings.open_high * 15;
|
|
2563
|
-
score -= Math.max(0, findings.open - findings.open_high) * 4;
|
|
2564
|
-
score -= tokenAudits.denied * 2;
|
|
2565
|
-
score += Math.min(actions.done, 20) * 0.4;
|
|
2566
|
-
score += Math.min(briefs.done, 10) * 0.5;
|
|
2567
|
-
score += Math.min(tokenAudits.granted, 20) * 0.2;
|
|
2568
|
-
score = Math.max(0, Math.min(100, Math.round(score)));
|
|
2569
|
-
const level = autonomyLevel(score);
|
|
2570
|
-
const status = score < 70 || findings.open_high || actions.guard_blocked ? "block" : (score < 85 || tokenAudits.denied ? "attention" : "ok");
|
|
2571
|
-
const factors = { actions, briefs, findings, token_audits: tokenAudits, note: "L5 is never automatic; critical work still requires explicit approval and a capability token." };
|
|
2572
|
-
let snapshotId = null;
|
|
2573
|
-
if (input.persist !== false) {
|
|
2574
|
-
const info = db.prepare("INSERT INTO autonomy_score_snapshot (scope, agent_name, score, autonomy_level, status, window_days, factors_json) VALUES (?,?,?,?,?,?,?)")
|
|
2575
|
-
.run(scopeName(input.scope), agent, score, level, status, days, safeJson(factors, {}));
|
|
2576
|
-
snapshotId = info.lastInsertRowid;
|
|
2577
|
-
}
|
|
2578
|
-
return { ok: true, agent_name: agent, score, autonomy_level: level, autonomy_meaning: autonomyMeaning(level), status, window_days: days, since, factors, snapshot_id: snapshotId };
|
|
2579
|
-
}
|
|
2580
|
-
|
|
2581
|
-
function normalizeProject(value) {
|
|
2582
|
-
return String(compactContent(value, 160) || "").trim();
|
|
2583
|
-
}
|
|
2584
|
-
|
|
2585
|
-
function normalizeSurface(value) {
|
|
2586
|
-
const text = String(compactContent(value, 160) || "").trim();
|
|
2587
|
-
return text || null;
|
|
2588
|
-
}
|
|
2589
|
-
|
|
2590
|
-
function normalizePriority(value) {
|
|
2591
|
-
const clean = String(value || "normal").trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2592
|
-
if (["critical", "high", "normal", "medium", "low", "backlog"].includes(clean)) return clean === "medium" ? "normal" : clean;
|
|
2593
|
-
return "normal";
|
|
2594
|
-
}
|
|
2595
|
-
|
|
2596
|
-
function normalizeTaskStatus(value) {
|
|
2597
|
-
const clean = String(value || "open").trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2598
|
-
if (["open", "active", "in_progress", "in-progress", "blocked", "needs_review", "needs-review", "done", "closed", "cancelled"].includes(clean)) {
|
|
2599
|
-
return clean.replace(/-/g, "_");
|
|
2600
|
-
}
|
|
2601
|
-
return "open";
|
|
2602
|
-
}
|
|
2603
|
-
|
|
2604
|
-
function normalizeIntentKind(value) {
|
|
2605
|
-
return String(value || "request").trim().toLowerCase().replace(/[^a-z0-9._:-]+/g, "_").replace(/^_+|_+$/g, "") || "request";
|
|
2606
|
-
}
|
|
2607
|
-
|
|
2608
|
-
function inferUserIntentPriority(text, fallback) {
|
|
2609
|
-
const src = String(text || "").toLowerCase();
|
|
2610
|
-
if (/demo[- ]?blocker|production down|502|kaputt|sofort|heute|blocker|dringend|urgent|kritisch/.test(src)) return "critical";
|
|
2611
|
-
if (/wichtig|high|bald|morgen/.test(src)) return "high";
|
|
2612
|
-
return normalizePriority(fallback);
|
|
2613
|
-
}
|
|
2614
|
-
|
|
2615
|
-
function rowToProjectFocus(row) {
|
|
2616
|
-
return row ? Object.assign({}, row, {
|
|
2617
|
-
must_do: parseJson(row.must_do_json, []),
|
|
2618
|
-
must_not_do: parseJson(row.must_not_do_json, []),
|
|
2619
|
-
meta: parseJson(row.meta_json, {}),
|
|
2620
|
-
}) : null;
|
|
2621
|
-
}
|
|
2622
|
-
|
|
2623
|
-
function rowToProjectTask(row) {
|
|
2624
|
-
return row ? Object.assign({}, row, {
|
|
2625
|
-
acceptance: parseJson(row.acceptance_json, []),
|
|
2626
|
-
blockers: parseJson(row.blockers_json, []),
|
|
2627
|
-
evidence: parseJson(row.evidence_json, []),
|
|
2628
|
-
meta: parseJson(row.meta_json, {}),
|
|
2629
|
-
}) : null;
|
|
2630
|
-
}
|
|
2631
|
-
|
|
2632
|
-
function rowToUserIntent(row) {
|
|
2633
|
-
return row ? Object.assign({}, row, { meta: parseJson(row.meta_json, {}) }) : null;
|
|
2634
|
-
}
|
|
2635
|
-
|
|
2636
|
-
function rowToProjectChannelPolicy(row) {
|
|
2637
|
-
return row ? Object.assign({}, row, {
|
|
2638
|
-
rules: parseJson(row.rules_json, []),
|
|
2639
|
-
meta: parseJson(row.meta_json, {}),
|
|
2640
|
-
}) : null;
|
|
2641
|
-
}
|
|
2642
|
-
|
|
2643
|
-
function projectFocusSet(db, input = {}) {
|
|
2644
|
-
ensureAgentGovernanceSchema(db);
|
|
2645
|
-
const scope = scopeName(input.scope);
|
|
2646
|
-
const project = normalizeProject(input.project || input.name);
|
|
2647
|
-
if (!project) return { error: "project required" };
|
|
2648
|
-
const summary = textOrNull(input.focus_summary || input.summary || input.objective, 4000);
|
|
2649
|
-
const activeTarget = textOrNull(input.active_target || input.target || input.current_target, 1000);
|
|
2650
|
-
db.prepare(`
|
|
2651
|
-
INSERT INTO project_focus
|
|
2652
|
-
(scope, project, surface, active_target, focus_summary, status, owner_agent, coordinator_agent, current_work_order_id, must_do_json, must_not_do_json, source_ref, meta_json, updated_by, updated_at)
|
|
2653
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
2654
|
-
ON CONFLICT(scope, project) DO UPDATE SET
|
|
2655
|
-
surface=excluded.surface,
|
|
2656
|
-
active_target=excluded.active_target,
|
|
2657
|
-
focus_summary=excluded.focus_summary,
|
|
2658
|
-
status=excluded.status,
|
|
2659
|
-
owner_agent=excluded.owner_agent,
|
|
2660
|
-
coordinator_agent=excluded.coordinator_agent,
|
|
2661
|
-
current_work_order_id=excluded.current_work_order_id,
|
|
2662
|
-
must_do_json=excluded.must_do_json,
|
|
2663
|
-
must_not_do_json=excluded.must_not_do_json,
|
|
2664
|
-
source_ref=excluded.source_ref,
|
|
2665
|
-
meta_json=excluded.meta_json,
|
|
2666
|
-
updated_by=excluded.updated_by,
|
|
2667
|
-
updated_at=excluded.updated_at
|
|
2668
|
-
`).run(
|
|
2669
|
-
scope,
|
|
2670
|
-
project,
|
|
2671
|
-
normalizeSurface(input.surface || input.portal || input.area),
|
|
2672
|
-
activeTarget,
|
|
2673
|
-
summary,
|
|
2674
|
-
input.status || "active",
|
|
2675
|
-
normalizeAgentName(input.owner_agent || input.owner || "") || null,
|
|
2676
|
-
normalizeAgentName(input.coordinator_agent || input.coordinator || "") || null,
|
|
2677
|
-
input.current_work_order_id || input.work_order_id || null,
|
|
2678
|
-
safeJson(listInput(input.must_do || input.must || input.acceptance), []),
|
|
2679
|
-
safeJson(listInput(input.must_not_do || input.out_of_scope || input.exclusions), []),
|
|
2680
|
-
textOrNull(input.source_ref || input.source || input.message_ref, 1000),
|
|
2681
|
-
safeJson(input.meta || {}, {}),
|
|
2682
|
-
normalizeAgentName(input.updated_by || input.agent_name || "") || null
|
|
2683
|
-
);
|
|
2684
|
-
return projectFocusGet(db, { scope, project });
|
|
2685
|
-
}
|
|
2686
|
-
|
|
2687
|
-
function projectFocusGet(db, input = {}) {
|
|
2688
|
-
ensureAgentGovernanceSchema(db);
|
|
2689
|
-
const scope = scopeName(input.scope);
|
|
2690
|
-
const project = normalizeProject(input.project || input.name);
|
|
2691
|
-
if (!project) return { error: "project required" };
|
|
2692
|
-
const row = db.prepare("SELECT * FROM project_focus WHERE scope=? AND project=?").get(scope, project);
|
|
2693
|
-
return { ok: !!row, focus: rowToProjectFocus(row), hint: row ? undefined : "Set focus with mem_project_focus_set before assigning project work." };
|
|
2694
|
-
}
|
|
2695
|
-
|
|
2696
|
-
function projectFocusList(db, input = {}) {
|
|
2697
|
-
ensureAgentGovernanceSchema(db);
|
|
2698
|
-
const where = ["scope=?"];
|
|
2699
|
-
const params = [scopeName(input.scope)];
|
|
2700
|
-
if (input.status) { where.push("status=?"); params.push(input.status); }
|
|
2701
|
-
params.push(clampInt(input.limit, 50, 1, 200));
|
|
2702
|
-
const rows = db.prepare(`SELECT * FROM project_focus WHERE ${where.join(" AND ")} ORDER BY updated_at DESC LIMIT ?`).all(...params).map(rowToProjectFocus);
|
|
2703
|
-
return { ok: true, count: rows.length, focuses: rows };
|
|
2704
|
-
}
|
|
2705
|
-
|
|
2706
|
-
function projectTaskCreate(db, input = {}) {
|
|
2707
|
-
ensureAgentGovernanceSchema(db);
|
|
2708
|
-
const scope = scopeName(input.scope);
|
|
2709
|
-
const project = normalizeProject(input.project || input.name);
|
|
2710
|
-
const title = textOrNull(input.title || input.summary || input.objective, 300);
|
|
2711
|
-
if (!project) return { error: "project required" };
|
|
2712
|
-
if (!title) return { error: "title required" };
|
|
2713
|
-
const summary = textOrNull(input.summary || input.objective || input.description || input.task, 4000);
|
|
2714
|
-
const info = db.prepare(`
|
|
2715
|
-
INSERT INTO project_task
|
|
2716
|
-
(scope, project, surface, title, summary, category, priority, status, owner_agent, assigned_agent, source_kind, source_id, source_ref, acceptance_json, blockers_json, evidence_json, linked_work_order_id, meta_json, created_by, updated_by)
|
|
2717
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
2718
|
-
`).run(
|
|
2719
|
-
scope,
|
|
2720
|
-
project,
|
|
2721
|
-
normalizeSurface(input.surface || input.portal || input.area),
|
|
2722
|
-
title,
|
|
2723
|
-
summary,
|
|
2724
|
-
normalizeIntentKind(input.category || input.kind || "task"),
|
|
2725
|
-
normalizePriority(input.priority),
|
|
2726
|
-
normalizeTaskStatus(input.status || "open"),
|
|
2727
|
-
normalizeAgentName(input.owner_agent || input.owner || "") || null,
|
|
2728
|
-
normalizeAgentName(input.assigned_agent || input.agent_name || input.assignee || "") || null,
|
|
2729
|
-
textOrNull(input.source_kind || input.source, 120),
|
|
2730
|
-
textOrNull(input.source_id || input.brief_id || input.finding_id, 120),
|
|
2731
|
-
textOrNull(input.source_ref || input.message_ref || input.url, 1000),
|
|
2732
|
-
safeJson(listInput(input.acceptance || input.acceptance_criteria || input.done_criteria), []),
|
|
2733
|
-
safeJson(listInput(input.blockers || input.blocked_by), []),
|
|
2734
|
-
safeJson(Array.isArray(input.evidence) ? input.evidence : [], []),
|
|
2735
|
-
input.linked_work_order_id || input.work_order_id || null,
|
|
2736
|
-
safeJson(input.meta || {}, {}),
|
|
2737
|
-
normalizeAgentName(input.created_by || input.agent_name || "") || null,
|
|
2738
|
-
normalizeAgentName(input.updated_by || input.agent_name || input.created_by || "") || null
|
|
2739
|
-
);
|
|
2740
|
-
return { ok: true, task: rowToProjectTask(db.prepare("SELECT * FROM project_task WHERE id=?").get(info.lastInsertRowid)) };
|
|
2741
|
-
}
|
|
2742
|
-
|
|
2743
|
-
function projectTaskUpdate(db, input = {}) {
|
|
2744
|
-
ensureAgentGovernanceSchema(db);
|
|
2745
|
-
const id = parseInt(input.id || input.task_id, 10);
|
|
2746
|
-
if (!id) return { error: "task_id required" };
|
|
2747
|
-
const existing = db.prepare("SELECT * FROM project_task WHERE id=?").get(id);
|
|
2748
|
-
if (!existing) return { error: "project_task_not_found", task_id: id };
|
|
2749
|
-
const updates = [];
|
|
2750
|
-
const params = [];
|
|
2751
|
-
function set(key, value) { updates.push(key + "=?"); params.push(value); }
|
|
2752
|
-
if (input.project !== undefined) set("project", normalizeProject(input.project));
|
|
2753
|
-
if (input.surface !== undefined || input.portal !== undefined || input.area !== undefined) set("surface", normalizeSurface(input.surface || input.portal || input.area));
|
|
2754
|
-
if (input.title !== undefined) set("title", textOrNull(input.title, 300));
|
|
2755
|
-
if (input.summary !== undefined || input.objective !== undefined || input.description !== undefined) set("summary", textOrNull(input.summary || input.objective || input.description, 4000));
|
|
2756
|
-
if (input.category !== undefined || input.kind !== undefined) set("category", normalizeIntentKind(input.category || input.kind));
|
|
2757
|
-
if (input.priority !== undefined) set("priority", normalizePriority(input.priority));
|
|
2758
|
-
if (input.status !== undefined) {
|
|
2759
|
-
const status = normalizeTaskStatus(input.status);
|
|
2760
|
-
set("status", status);
|
|
2761
|
-
if (["done", "closed", "cancelled"].includes(status)) set("completed_at", nowIso());
|
|
2762
|
-
}
|
|
2763
|
-
if (input.owner_agent !== undefined || input.owner !== undefined) set("owner_agent", normalizeAgentName(input.owner_agent || input.owner || "") || null);
|
|
2764
|
-
if (input.assigned_agent !== undefined || input.agent_name !== undefined || input.assignee !== undefined) set("assigned_agent", normalizeAgentName(input.assigned_agent || input.agent_name || input.assignee || "") || null);
|
|
2765
|
-
if (input.acceptance !== undefined || input.acceptance_criteria !== undefined || input.done_criteria !== undefined) set("acceptance_json", safeJson(listInput(input.acceptance || input.acceptance_criteria || input.done_criteria), []));
|
|
2766
|
-
if (input.blockers !== undefined || input.blocked_by !== undefined) set("blockers_json", safeJson(listInput(input.blockers || input.blocked_by), []));
|
|
2767
|
-
if (input.evidence !== undefined) set("evidence_json", safeJson(Array.isArray(input.evidence) ? input.evidence : [], []));
|
|
2768
|
-
if (input.linked_work_order_id !== undefined || input.work_order_id !== undefined) set("linked_work_order_id", input.linked_work_order_id || input.work_order_id || null);
|
|
2769
|
-
if (input.meta !== undefined) set("meta_json", safeJson(input.meta || {}, {}));
|
|
2770
|
-
set("updated_by", normalizeAgentName(input.updated_by || input.agent_name || "") || null);
|
|
2771
|
-
set("updated_at", nowIso());
|
|
2772
|
-
params.push(id);
|
|
2773
|
-
db.prepare(`UPDATE project_task SET ${updates.join(", ")} WHERE id=?`).run(...params);
|
|
2774
|
-
return { ok: true, task: rowToProjectTask(db.prepare("SELECT * FROM project_task WHERE id=?").get(id)) };
|
|
2775
|
-
}
|
|
2776
|
-
|
|
2777
|
-
function projectTaskList(db, input = {}) {
|
|
2778
|
-
ensureAgentGovernanceSchema(db);
|
|
2779
|
-
const where = ["scope=?"];
|
|
2780
|
-
const params = [scopeName(input.scope)];
|
|
2781
|
-
const project = normalizeProject(input.project || input.name);
|
|
2782
|
-
if (project) { where.push("project=?"); params.push(project); }
|
|
2783
|
-
if (input.assigned_agent || input.agent_name) { where.push("assigned_agent=?"); params.push(normalizeAgentName(input.assigned_agent || input.agent_name)); }
|
|
2784
|
-
if (input.owner_agent || input.owner) { where.push("owner_agent=?"); params.push(normalizeAgentName(input.owner_agent || input.owner)); }
|
|
2785
|
-
if (input.status) { where.push("status=?"); params.push(normalizeTaskStatus(input.status)); }
|
|
2786
|
-
else if (input.include_done !== true) where.push("status NOT IN ('done','closed','cancelled')");
|
|
2787
|
-
params.push(clampInt(input.limit, 50, 1, 500));
|
|
2788
|
-
const rows = db.prepare(`SELECT * FROM project_task WHERE ${where.join(" AND ")} ORDER BY CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'normal' THEN 2 WHEN 'low' THEN 3 ELSE 4 END, updated_at DESC LIMIT ?`).all(...params).map(rowToProjectTask);
|
|
2789
|
-
return { ok: true, count: rows.length, tasks: rows };
|
|
2790
|
-
}
|
|
2791
|
-
|
|
2792
|
-
function normalizeBriefHeading(value) {
|
|
2793
|
-
return String(value || "")
|
|
2794
|
-
.trim()
|
|
2795
|
-
.toLowerCase()
|
|
2796
|
-
.replace(/[`*_()[\]{}:.,!?]+/g, "")
|
|
2797
|
-
.replace(/\s+/g, " ");
|
|
2798
|
-
}
|
|
2799
|
-
|
|
2800
|
-
function markdownSection(content, names) {
|
|
2801
|
-
const wanted = new Set((Array.isArray(names) ? names : [names]).map(normalizeBriefHeading));
|
|
2802
|
-
const lines = String(content || "").split(/\r?\n/);
|
|
2803
|
-
let start = -1;
|
|
2804
|
-
let level = 99;
|
|
2805
|
-
for (let i = 0; i < lines.length; i++) {
|
|
2806
|
-
const match = /^(#{1,6})\s+(.+?)\s*$/.exec(lines[i]);
|
|
2807
|
-
if (!match) continue;
|
|
2808
|
-
const heading = normalizeBriefHeading(match[2]);
|
|
2809
|
-
if (wanted.has(heading)) {
|
|
2810
|
-
start = i + 1;
|
|
2811
|
-
level = match[1].length;
|
|
2812
|
-
break;
|
|
2813
|
-
}
|
|
2814
|
-
}
|
|
2815
|
-
if (start < 0) return null;
|
|
2816
|
-
const out = [];
|
|
2817
|
-
for (let i = start; i < lines.length; i++) {
|
|
2818
|
-
const match = /^(#{1,6})\s+/.exec(lines[i]);
|
|
2819
|
-
if (match && match[1].length <= level) break;
|
|
2820
|
-
out.push(lines[i]);
|
|
2821
|
-
}
|
|
2822
|
-
return textOrNull(out.join("\n").trim(), 4000);
|
|
2823
|
-
}
|
|
2824
|
-
|
|
2825
|
-
function firstMarkdownTitle(content) {
|
|
2826
|
-
for (const line of String(content || "").split(/\r?\n/)) {
|
|
2827
|
-
const match = /^#{1,6}\s+(.+?)\s*$/.exec(line);
|
|
2828
|
-
if (match) {
|
|
2829
|
-
const title = textOrNull(match[1].replace(/^brief\s*[:#-]?\s*/i, ""), 260);
|
|
2830
|
-
if (title) return title;
|
|
2831
|
-
}
|
|
2832
|
-
}
|
|
2833
|
-
return null;
|
|
2834
|
-
}
|
|
2835
|
-
|
|
2836
|
-
function briefPreview(content, max = 700) {
|
|
2837
|
-
return textOrNull(String(content || "")
|
|
2838
|
-
.replace(/^#{1,6}\s+.+$/gm, "")
|
|
2839
|
-
.replace(/^\s*[-*+]\s*/gm, "")
|
|
2840
|
-
.replace(/\s+/g, " ")
|
|
2841
|
-
.trim(), max);
|
|
2842
|
-
}
|
|
2843
|
-
|
|
2844
|
-
function sectionList(content, names) {
|
|
2845
|
-
const section = markdownSection(content, names);
|
|
2846
|
-
if (!section) return [];
|
|
2847
|
-
const items = [];
|
|
2848
|
-
for (const line of section.split(/\r?\n/)) {
|
|
2849
|
-
const match = /^\s*(?:[-*+]|\d+[.)]|[•])\s+(.+?)\s*$/.exec(line);
|
|
2850
|
-
if (match && match[1]) items.push(match[1].trim());
|
|
2851
|
-
}
|
|
2852
|
-
return items.length ? uniqueStrings(items) : listInput(section);
|
|
2853
|
-
}
|
|
2854
|
-
|
|
2855
|
-
function briefMeta(row) {
|
|
2856
|
-
return parseJson(row && row.meta_json, {});
|
|
2857
|
-
}
|
|
2858
|
-
|
|
2859
|
-
function extractBriefFiles(content, meta = {}) {
|
|
2860
|
-
const files = [];
|
|
2861
|
-
const add = (value) => {
|
|
2862
|
-
for (const item of listInput(value)) {
|
|
2863
|
-
if (/[\\/]/.test(item) || /\.[A-Za-z0-9]{1,8}$/.test(item)) files.push(item);
|
|
2864
|
-
}
|
|
2865
|
-
};
|
|
2866
|
-
add(meta.files);
|
|
2867
|
-
add(meta.file_paths);
|
|
2868
|
-
add(meta.paths);
|
|
2869
|
-
const text = String(content || "");
|
|
2870
|
-
for (const match of text.matchAll(/`([^`\n]*(?:[\\/][^`\n]+|\.[A-Za-z0-9]{1,8})[^`\n]*)`/g)) add(match[1]);
|
|
2871
|
-
for (const match of text.matchAll(/(?:[A-Za-z]:\\)?[A-Za-z0-9_. -]+[\\/][A-Za-z0-9_.() \\/-]+\.[A-Za-z0-9]{1,8}/g)) add(match[0]);
|
|
2872
|
-
return uniqueStrings(files).sort();
|
|
2873
|
-
}
|
|
2874
|
-
|
|
2875
|
-
function briefTaskDedupeKey(scope, draft) {
|
|
2876
|
-
const basis = {
|
|
2877
|
-
scope,
|
|
2878
|
-
project: draft.project || "",
|
|
2879
|
-
category: draft.category || "",
|
|
2880
|
-
title: String(draft.title || "").trim().toLowerCase(),
|
|
2881
|
-
files: Array.isArray(draft.files) ? draft.files.slice().sort() : [],
|
|
2882
|
-
};
|
|
2883
|
-
return { key: sha(JSON.stringify(basis)), basis };
|
|
2884
|
-
}
|
|
2885
|
-
|
|
2886
|
-
function briefToTaskDraft(row, input = {}) {
|
|
2887
|
-
const meta = briefMeta(row);
|
|
2888
|
-
const content = String(row && row.content || "");
|
|
2889
|
-
const explicitProject = input.project || input.default_project || meta.project || meta.project_name || markdownSection(content, ["project", "projekt", "scope"]);
|
|
2890
|
-
const project = normalizeProject(explicitProject);
|
|
2891
|
-
const title = textOrNull(
|
|
2892
|
-
input.title ||
|
|
2893
|
-
meta.title ||
|
|
2894
|
-
markdownSection(content, ["title", "titel", "task", "aufgabe", "finding", "befund"]) ||
|
|
2895
|
-
firstMarkdownTitle(content) ||
|
|
2896
|
-
briefPreview(content, 220),
|
|
2897
|
-
260
|
|
2898
|
-
);
|
|
2899
|
-
const summary = textOrNull(
|
|
2900
|
-
input.summary ||
|
|
2901
|
-
meta.summary ||
|
|
2902
|
-
markdownSection(content, ["request", "anforderung", "summary", "zusammenfassung", "description", "beschreibung", "details", "befund"]) ||
|
|
2903
|
-
briefPreview(content, 1200),
|
|
2904
|
-
4000
|
|
2905
|
-
);
|
|
2906
|
-
const files = extractBriefFiles(content, meta);
|
|
2907
|
-
const acceptance = listInput(input.acceptance || input.acceptance_criteria || meta.acceptance || meta.acceptance_criteria)
|
|
2908
|
-
.concat(sectionList(content, ["acceptance", "akzeptanz", "done", "definition of done", "annahme", "verify", "verification", "test"]));
|
|
2909
|
-
const priorityText = input.priority || meta.priority || markdownSection(content, ["priority", "priorität", "prio"]) || content;
|
|
2910
|
-
const category = normalizeIntentKind(input.category || input.kind || meta.category || meta.kind || "brief");
|
|
2911
|
-
const draft = {
|
|
2912
|
-
project,
|
|
2913
|
-
title,
|
|
2914
|
-
summary,
|
|
2915
|
-
category,
|
|
2916
|
-
priority: inferUserIntentPriority(priorityText + " " + title + " " + summary, input.priority || meta.priority),
|
|
2917
|
-
status: normalizeTaskStatus(input.task_status || input.status_for_task || "open"),
|
|
2918
|
-
owner_agent: normalizeAgentName(input.owner_agent || input.owner || meta.owner_agent || row.agent_name || "") || null,
|
|
2919
|
-
assigned_agent: normalizeAgentName(input.assigned_agent || input.agent_name || input.assignee || meta.assigned_agent || row.agent_name || "") || null,
|
|
2920
|
-
acceptance: uniqueStrings(acceptance),
|
|
2921
|
-
blockers: listInput(input.blockers || meta.blockers),
|
|
2922
|
-
files,
|
|
2923
|
-
source_kind: "agent_brief",
|
|
2924
|
-
source_id: String(row.id),
|
|
2925
|
-
source_ref: input.source_ref || meta.source_ref || ("agent_brief:" + row.id),
|
|
2926
|
-
created_by: normalizeAgentName(input.created_by || input.agent_name || row.source_agent || "mnemo") || "mnemo",
|
|
2927
|
-
meta: Object.assign({}, meta, {
|
|
2928
|
-
brief_id: row.id,
|
|
2929
|
-
brief_agent_name: row.agent_name || null,
|
|
2930
|
-
brief_source_agent: row.source_agent || null,
|
|
2931
|
-
files,
|
|
2932
|
-
}),
|
|
2933
|
-
};
|
|
2934
|
-
return draft;
|
|
2935
|
-
}
|
|
2936
|
-
|
|
2937
|
-
function insertProjectTaskIngest(db, values) {
|
|
2938
|
-
db.prepare(`
|
|
2939
|
-
INSERT OR IGNORE INTO project_task_ingest
|
|
2940
|
-
(scope, source_kind, source_id, dedupe_key, task_id, project, brief_id, status, meta_json, updated_at)
|
|
2941
|
-
VALUES (?,?,?,?,?,?,?,?,?,strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
2942
|
-
`).run(
|
|
2943
|
-
values.scope,
|
|
2944
|
-
values.source_kind,
|
|
2945
|
-
String(values.source_id),
|
|
2946
|
-
values.dedupe_key,
|
|
2947
|
-
values.task_id,
|
|
2948
|
-
values.project || null,
|
|
2949
|
-
values.brief_id || null,
|
|
2950
|
-
values.status || "linked",
|
|
2951
|
-
safeJson(values.meta || {}, {})
|
|
2952
|
-
);
|
|
2953
|
-
}
|
|
2954
|
-
|
|
2955
|
-
function markAgentBriefStatus(db, briefId, status, outcome) {
|
|
2956
|
-
const clean = textOrNull(status, 80);
|
|
2957
|
-
if (!clean || !briefId) return;
|
|
2958
|
-
try {
|
|
2959
|
-
const cols = db.prepare("PRAGMA table_info(agent_brief)").all().map((col) => col.name);
|
|
2960
|
-
if (cols.includes("done_at") && ["done", "failed"].includes(clean)) {
|
|
2961
|
-
if (cols.includes("outcome")) {
|
|
2962
|
-
db.prepare("UPDATE agent_brief SET status=?, done_at=?, outcome=COALESCE(outcome, ?) WHERE id=?")
|
|
2963
|
-
.run(clean, nowIso(), outcome || "Converted to project task.", briefId);
|
|
2964
|
-
} else {
|
|
2965
|
-
db.prepare("UPDATE agent_brief SET status=?, done_at=? WHERE id=?").run(clean, nowIso(), briefId);
|
|
2966
|
-
}
|
|
2967
|
-
} else {
|
|
2968
|
-
db.prepare("UPDATE agent_brief SET status=? WHERE id=?").run(clean, briefId);
|
|
2969
|
-
}
|
|
2970
|
-
} catch {}
|
|
2971
|
-
}
|
|
2972
|
-
|
|
2973
|
-
function ingestOneBriefAsTask(db, row, input = {}) {
|
|
2974
|
-
const scope = scopeName(input.scope);
|
|
2975
|
-
const sourceId = String(row.id);
|
|
2976
|
-
const existingSource = db.prepare("SELECT * FROM project_task_ingest WHERE scope=? AND source_kind='agent_brief' AND source_id=?").get(scope, sourceId);
|
|
2977
|
-
if (existingSource) {
|
|
2978
|
-
const task = db.prepare("SELECT * FROM project_task WHERE id=?").get(existingSource.task_id);
|
|
2979
|
-
if (input.mark_brief_status) markAgentBriefStatus(db, row.id, input.mark_brief_status, "Already converted to project task #" + existingSource.task_id + ".");
|
|
2980
|
-
return { action: "source_existing", brief_id: row.id, task: rowToProjectTask(task), task_id: existingSource.task_id };
|
|
2981
|
-
}
|
|
2982
|
-
|
|
2983
|
-
const draft = briefToTaskDraft(row, input);
|
|
2984
|
-
if (!draft.project || !draft.title) {
|
|
2985
|
-
return { action: "skipped", reason: !draft.project ? "project_missing" : "title_missing", brief_id: row.id };
|
|
2986
|
-
}
|
|
2987
|
-
const dedupe = briefTaskDedupeKey(scope, draft);
|
|
2988
|
-
draft.meta.dedupe_key = dedupe.key;
|
|
2989
|
-
draft.meta.dedupe_basis = dedupe.basis;
|
|
2990
|
-
|
|
2991
|
-
if (input.dry_run === true) {
|
|
2992
|
-
return { action: "dry_run", brief_id: row.id, dedupe_key: dedupe.key, draft };
|
|
2993
|
-
}
|
|
2994
|
-
|
|
2995
|
-
let task = null;
|
|
2996
|
-
let action = "created";
|
|
2997
|
-
const mapped = db.prepare("SELECT * FROM project_task_ingest WHERE scope=? AND dedupe_key=? ORDER BY updated_at DESC LIMIT 1").get(scope, dedupe.key);
|
|
2998
|
-
if (mapped) {
|
|
2999
|
-
task = db.prepare("SELECT * FROM project_task WHERE id=?").get(mapped.task_id);
|
|
3000
|
-
action = "linked_duplicate";
|
|
3001
|
-
}
|
|
3002
|
-
if (!task) {
|
|
3003
|
-
const where = ["scope=?", "project=?", "title=?"];
|
|
3004
|
-
const params = [scope, draft.project, draft.title];
|
|
3005
|
-
if (input.include_done !== true) where.push("status NOT IN ('done','closed','cancelled')");
|
|
3006
|
-
task = db.prepare(`SELECT * FROM project_task WHERE ${where.join(" AND ")} ORDER BY updated_at DESC LIMIT 1`).get(...params);
|
|
3007
|
-
if (task) action = "linked_existing";
|
|
3008
|
-
}
|
|
3009
|
-
if (!task) {
|
|
3010
|
-
const created = projectTaskCreate(db, Object.assign({}, draft, {
|
|
3011
|
-
scope,
|
|
3012
|
-
meta: draft.meta,
|
|
3013
|
-
}));
|
|
3014
|
-
task = created.task;
|
|
3015
|
-
action = "created";
|
|
3016
|
-
} else {
|
|
3017
|
-
task = rowToProjectTask(task);
|
|
3018
|
-
}
|
|
3019
|
-
|
|
3020
|
-
insertProjectTaskIngest(db, {
|
|
3021
|
-
scope,
|
|
3022
|
-
source_kind: "agent_brief",
|
|
3023
|
-
source_id: sourceId,
|
|
3024
|
-
dedupe_key: dedupe.key,
|
|
3025
|
-
task_id: task.id,
|
|
3026
|
-
project: draft.project,
|
|
3027
|
-
brief_id: row.id,
|
|
3028
|
-
status: action,
|
|
3029
|
-
meta: { dedupe_basis: dedupe.basis, linked_from_agent_brief: row.id },
|
|
3030
|
-
});
|
|
3031
|
-
if (input.mark_brief_status) {
|
|
3032
|
-
markAgentBriefStatus(db, row.id, input.mark_brief_status, "Converted to project task #" + task.id + ".");
|
|
3033
|
-
}
|
|
3034
|
-
return { action, brief_id: row.id, dedupe_key: dedupe.key, task };
|
|
3035
|
-
}
|
|
3036
|
-
|
|
3037
|
-
function briefTaskIngest(db, input = {}) {
|
|
3038
|
-
ensureAgentGovernanceSchema(db);
|
|
3039
|
-
if (!tableExists(db, "agent_brief")) return { ok: true, count: 0, created: 0, linked: 0, skipped: 0, results: [], note: "agent_brief table missing" };
|
|
3040
|
-
const scope = scopeName(input.scope);
|
|
3041
|
-
const where = [];
|
|
3042
|
-
const params = [];
|
|
3043
|
-
const briefIds = uniqueStrings([].concat(input.brief_ids || [], input.brief_id || []).filter((id) => id !== undefined && id !== null)).map((id) => parseInt(id, 10)).filter(Boolean);
|
|
3044
|
-
if (briefIds.length) {
|
|
3045
|
-
where.push(`id IN (${briefIds.map(() => "?").join(",")})`);
|
|
3046
|
-
params.push(...briefIds);
|
|
3047
|
-
} else {
|
|
3048
|
-
const statuses = listInput(input.statuses || input.status || ["pending", "dispatched"]);
|
|
3049
|
-
if (statuses.length && input.include_all_statuses !== true) {
|
|
3050
|
-
where.push(`status IN (${statuses.map(() => "?").join(",")})`);
|
|
3051
|
-
params.push(...statuses);
|
|
3052
|
-
}
|
|
3053
|
-
if (input.agent_name) { where.push("agent_name=?"); params.push(normalizeAgentName(input.agent_name)); }
|
|
3054
|
-
if (input.source_agent) { where.push("source_agent=?"); params.push(normalizeAgentName(input.source_agent)); }
|
|
3055
|
-
const project = normalizeProject(input.project || input.default_project);
|
|
3056
|
-
if (project) {
|
|
3057
|
-
where.push("(content LIKE ? OR (json_valid(meta_json) AND json_extract(meta_json,'$.project')=?))");
|
|
3058
|
-
params.push("%" + project + "%", project);
|
|
3059
|
-
}
|
|
3060
|
-
}
|
|
3061
|
-
const limit = clampInt(input.limit, 20, 1, 200);
|
|
3062
|
-
params.push(limit);
|
|
3063
|
-
const rows = db.prepare(`SELECT id, agent_name, source_agent, content, status, created_at, meta_json FROM agent_brief ${where.length ? "WHERE " + where.join(" AND ") : ""} ORDER BY created_at ASC, id ASC LIMIT ?`).all(...params);
|
|
3064
|
-
const results = rows.map((row) => ingestOneBriefAsTask(db, row, Object.assign({}, input, { scope })));
|
|
3065
|
-
return {
|
|
3066
|
-
ok: true,
|
|
3067
|
-
count: results.length,
|
|
3068
|
-
created: results.filter((result) => result.action === "created").length,
|
|
3069
|
-
linked: results.filter((result) => result.action === "linked_duplicate" || result.action === "linked_existing" || result.action === "source_existing").length,
|
|
3070
|
-
skipped: results.filter((result) => result.action === "skipped").length,
|
|
3071
|
-
dry_run: input.dry_run === true,
|
|
3072
|
-
results,
|
|
3073
|
-
};
|
|
3074
|
-
}
|
|
3075
|
-
|
|
3076
|
-
function gstackCatalog(input = {}) {
|
|
3077
|
-
const includePowerTools = input.include_power_tools !== false;
|
|
3078
|
-
return {
|
|
3079
|
-
ok: true,
|
|
3080
|
-
source: "gstack-inspired Mnemo Agent OS operating model",
|
|
3081
|
-
principle: "gstack supplies workflow, specialist roles, and operating rhythm; Mnemo remains memory, claims, rules, audit, evidence, and company truth.",
|
|
3082
|
-
kernel: MNEMO_AGENT_OS_KERNEL,
|
|
3083
|
-
workflow: GSTACK_WORKFLOW,
|
|
3084
|
-
role_count: GSTACK_ROLE_CATALOG.length,
|
|
3085
|
-
roles: GSTACK_ROLE_CATALOG,
|
|
3086
|
-
core_skill_count: GSTACK_CORE_SKILLS.length,
|
|
3087
|
-
core_skills: GSTACK_CORE_SKILLS,
|
|
3088
|
-
power_tools: includePowerTools ? GSTACK_POWER_TOOLS : [],
|
|
3089
|
-
commands: MNEMO_OS_COMMANDS,
|
|
3090
|
-
gates: {
|
|
3091
|
-
pre_work: ["understand_problem", "check_existing_work", "check_rules", "load_portal_context", "check_dependencies", "set_work_claim", "save_plan"],
|
|
3092
|
-
during_work: ["document_each_change", "mark_affected_portals", "save_decisions", "save_tests", "save_risks"],
|
|
3093
|
-
post_work: ["save_result", "save_evidence", "write_handoff", "mark_open_points", "produce_telegram_or_brief_update", "memorize_done_state"]
|
|
3094
|
-
},
|
|
3095
|
-
prevention_engines: ["duplicate_task_detection", "never_again_scar_check", "owner_rule_validation", "protected_scope_validation", "completion_guard"],
|
|
3096
|
-
multiportal_rule_layers: ["global_rules", "portal_rules", "customer_partner_rules"],
|
|
3097
|
-
hundred_agent_model: {
|
|
3098
|
-
shard_by: ["portal", "role", "project", "risk_class"],
|
|
3099
|
-
requires: ["role assignment", "portal context", "work claim", "capability token for risky work", "evidence receipts", "handoff"],
|
|
3100
|
-
coordination_views: ["mem_agent_company_board", "mem_project_board", "mem_work_report_feed", "mem_project_timeline_report"]
|
|
3101
|
-
}
|
|
3102
|
-
};
|
|
3103
|
-
}
|
|
3104
|
-
|
|
3105
|
-
function agentRoleSelect(db, input = {}) {
|
|
3106
|
-
if (!input.agent_name || !input.role) return { error: "agent_name + role required" };
|
|
3107
|
-
ensureAgentGovernanceSchema(db);
|
|
3108
|
-
const role = gstackRole(input.role);
|
|
3109
|
-
if (!role) return { error: "unknown_role", role: input.role, allowed_roles: GSTACK_ROLE_CATALOG.map((row) => row.role) };
|
|
3110
|
-
const scope = scopeName(input.scope);
|
|
3111
|
-
const agent = normalizeAgentName(input.agent_name);
|
|
3112
|
-
const project = textOrNull(input.project, 500);
|
|
3113
|
-
const portal = input.portal || input.portal_name || input.surface ? portalName(input) : null;
|
|
3114
|
-
if (input.replace_active !== false) {
|
|
3115
|
-
const where = ["scope=?", "agent_name=?", "status='active'"];
|
|
3116
|
-
const params = [scope, agent];
|
|
3117
|
-
if (project) { where.push("(project=? OR project IS NULL OR project='')"); params.push(project); }
|
|
3118
|
-
if (portal) { where.push("(portal=? OR portal IS NULL OR portal='')"); params.push(portal); }
|
|
3119
|
-
db.prepare("UPDATE agent_role_assignment SET status='superseded', updated_at=strftime('%Y-%m-%dT%H:%M:%fZ','now') WHERE " + where.join(" AND ")).run(...params);
|
|
3120
|
-
}
|
|
3121
|
-
const info = db.prepare("INSERT INTO agent_role_assignment (scope, project, portal, agent_name, role_name, department_name, task, plan_summary, status, selected_by, meta_json) VALUES (?,?,?,?,?,?,?,?,?,?,?)")
|
|
3122
|
-
.run(scope, project, portal, agent, role.role, role.department_name, textOrNull(input.task, 4000), textOrNull(input.plan || input.plan_summary, 8000), input.status || "active", input.selected_by || input.updated_by || agent, safeJson(input.meta || {}, {}));
|
|
3123
|
-
const assignment = rowToRoleAssignment(db.prepare("SELECT * FROM agent_role_assignment WHERE id=?").get(info.lastInsertRowid));
|
|
3124
|
-
try {
|
|
3125
|
-
db.prepare("INSERT INTO agent_action (agent_name, action_kind, target, status, payload_json, topic) VALUES (?, 'agent_role_select', ?, 'done', ?, 'agent_governance')")
|
|
3126
|
-
.run(agent, project || portal || role.role, JSON.stringify({ assignment_id: assignment.id, role: role.role, project, portal, task: input.task || null }));
|
|
3127
|
-
} catch {}
|
|
3128
|
-
return { ok: true, assignment, role_contract: role };
|
|
3129
|
-
}
|
|
3130
|
-
|
|
3131
|
-
function agentRoleGet(db, input = {}) {
|
|
3132
|
-
if (!input.agent_name) return { error: "agent_name required" };
|
|
3133
|
-
const assignment = latestRoleAssignment(db, input);
|
|
3134
|
-
return assignment ? { ok: true, assignment, role_contract: gstackRole(assignment.role_name) } : { error: "missing_role_assignment", agent_name: normalizeAgentName(input.agent_name), hint: "Call mem_agent_role_select before work starts." };
|
|
3135
|
-
}
|
|
3136
|
-
|
|
3137
|
-
function agentRoleList(db, input = {}) {
|
|
3138
|
-
ensureAgentGovernanceSchema(db);
|
|
3139
|
-
const scope = scopeName(input.scope);
|
|
3140
|
-
const where = ["scope=?"];
|
|
3141
|
-
const params = [scope];
|
|
3142
|
-
if (input.agent_name) { where.push("agent_name=?"); params.push(normalizeAgentName(input.agent_name)); }
|
|
3143
|
-
if (input.project) { where.push("project=?"); params.push(String(input.project)); }
|
|
3144
|
-
if (input.role) { where.push("role_name=?"); params.push(canonicalRoleName(input.role) || input.role); }
|
|
3145
|
-
if (input.status) { where.push("status=?"); params.push(String(input.status)); }
|
|
3146
|
-
else if (!input.include_inactive) where.push("status='active'");
|
|
3147
|
-
const rows = db.prepare("SELECT * FROM agent_role_assignment WHERE " + where.join(" AND ") + " ORDER BY updated_at DESC, id DESC LIMIT ?").all(...params, clampInt(input.limit, 100, 1, 1000)).map(rowToRoleAssignment);
|
|
3148
|
-
return { ok: true, count: rows.length, assignments: rows };
|
|
3149
|
-
}
|
|
3150
|
-
|
|
3151
|
-
function portalContextSet(db, input = {}) {
|
|
3152
|
-
if (!input.project) return { error: "project required" };
|
|
3153
|
-
ensureAgentGovernanceSchema(db);
|
|
3154
|
-
const scope = scopeName(input.scope);
|
|
3155
|
-
const project = String(input.project);
|
|
3156
|
-
const portal = portalName(input);
|
|
3157
|
-
const info = db.prepare(
|
|
3158
|
-
"INSERT INTO portal_context (scope, project, portal, portal_id, portal_name, company_name, brand_name, domain, environment, country_or_market_json, user_role, language, supported_languages_json, design_json, credit_system_json, pricing_json, rights_json, billing_owner, auth_owner, deployment_owner, legal_owner, forbidden_cross_portal_leaks_json, shared_modules_json, protected_surfaces_json, global_rules_json, portal_rules_json, customer_partner_rules_json, dependencies_json, status, updated_by, meta_json) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) " +
|
|
3159
|
-
"ON CONFLICT(scope, project, portal) DO UPDATE SET portal_id=excluded.portal_id, portal_name=excluded.portal_name, company_name=excluded.company_name, brand_name=excluded.brand_name, domain=excluded.domain, environment=excluded.environment, country_or_market_json=excluded.country_or_market_json, user_role=excluded.user_role, language=excluded.language, supported_languages_json=excluded.supported_languages_json, design_json=excluded.design_json, credit_system_json=excluded.credit_system_json, pricing_json=excluded.pricing_json, rights_json=excluded.rights_json, billing_owner=excluded.billing_owner, auth_owner=excluded.auth_owner, deployment_owner=excluded.deployment_owner, legal_owner=excluded.legal_owner, forbidden_cross_portal_leaks_json=excluded.forbidden_cross_portal_leaks_json, shared_modules_json=excluded.shared_modules_json, protected_surfaces_json=excluded.protected_surfaces_json, global_rules_json=excluded.global_rules_json, portal_rules_json=excluded.portal_rules_json, customer_partner_rules_json=excluded.customer_partner_rules_json, dependencies_json=excluded.dependencies_json, status=excluded.status, updated_by=excluded.updated_by, meta_json=excluded.meta_json, updated_at=strftime('%Y-%m-%dT%H:%M:%fZ','now')"
|
|
3160
|
-
).run(
|
|
3161
|
-
scope,
|
|
3162
|
-
project,
|
|
3163
|
-
portal,
|
|
3164
|
-
textOrNull(input.portal_id || portal, 500),
|
|
3165
|
-
textOrNull(input.portal_name || input.name || portal, 1000),
|
|
3166
|
-
textOrNull(input.company_name || input.company || input.company_or_brand || input.brand_owner, 1000),
|
|
3167
|
-
textOrNull(input.brand_name || input.brand || input.company_or_brand || input.brand_owner, 1000),
|
|
3168
|
-
textOrNull(input.domain, 1000),
|
|
3169
|
-
textOrNull(input.environment, 100),
|
|
3170
|
-
safeJson(jsonList(input.country_or_market || input.market || input.country), []),
|
|
3171
|
-
textOrNull(input.user_role || input.role_context, 1000),
|
|
3172
|
-
textOrNull(input.language || input.language_default || input.locale, 100),
|
|
3173
|
-
safeJson(jsonList(input.supported_languages || input.languages), []),
|
|
3174
|
-
safeJson(input.design || input.design_rules || input.theme_or_design_source || {}, {}),
|
|
3175
|
-
safeJson(input.credit_system || input.credits || {}, {}),
|
|
3176
|
-
safeJson(input.pricing || input.prices || input.pricing_source_of_truth || {}, {}),
|
|
3177
|
-
safeJson(input.rights || input.permissions || {}, {}),
|
|
3178
|
-
textOrNull(input.billing_owner, 500),
|
|
3179
|
-
textOrNull(input.auth_owner, 500),
|
|
3180
|
-
textOrNull(input.deployment_owner || input.deploy_owner, 500),
|
|
3181
|
-
textOrNull(input.legal_owner, 500),
|
|
3182
|
-
safeJson(jsonList(input.forbidden_cross_portal_leaks), []),
|
|
3183
|
-
safeJson(jsonList(input.shared_modules), []),
|
|
3184
|
-
safeJson(jsonList(input.protected_surfaces), []),
|
|
3185
|
-
safeJson(jsonList(input.global_rules), []),
|
|
3186
|
-
safeJson(jsonList(input.portal_rules || input.rules), []),
|
|
3187
|
-
safeJson(jsonList(input.customer_partner_rules || input.customer_rules || input.partner_rules), []),
|
|
3188
|
-
safeJson(jsonList(input.dependencies || input.required_checks), []),
|
|
3189
|
-
input.status || "active",
|
|
3190
|
-
input.updated_by || input.agent_name || "unknown",
|
|
3191
|
-
safeJson(input.meta || {}, {})
|
|
3192
|
-
);
|
|
3193
|
-
const context = rowToPortalContext(db.prepare("SELECT * FROM portal_context WHERE scope=? AND project=? AND portal=?").get(scope, project, portal));
|
|
3194
|
-
try {
|
|
3195
|
-
db.prepare("INSERT INTO agent_action (agent_name, action_kind, target, status, payload_json, topic) VALUES (?, 'portal_context_set', ?, 'done', ?, 'agent_governance')")
|
|
3196
|
-
.run(input.updated_by || input.agent_name || "unknown", project + ":" + portal, JSON.stringify({ portal_context_id: context.id, changes: info.changes }));
|
|
3197
|
-
} catch {}
|
|
3198
|
-
return { ok: true, context };
|
|
3199
|
-
}
|
|
3200
|
-
|
|
3201
|
-
function portalContextGet(db, input = {}) {
|
|
3202
|
-
if (!input.project) return { error: "project required" };
|
|
3203
|
-
ensureAgentGovernanceSchema(db);
|
|
3204
|
-
const scope = scopeName(input.scope);
|
|
3205
|
-
const project = String(input.project);
|
|
3206
|
-
const portal = portalName(input);
|
|
3207
|
-
let row = db.prepare("SELECT * FROM portal_context WHERE scope=? AND project=? AND portal=? AND status='active' ORDER BY updated_at DESC LIMIT 1").get(scope, project, portal);
|
|
3208
|
-
if (!row && portal !== "default") row = db.prepare("SELECT * FROM portal_context WHERE scope=? AND project=? AND portal='default' AND status='active' ORDER BY updated_at DESC LIMIT 1").get(scope, project);
|
|
3209
|
-
return row ? { ok: true, context: rowToPortalContext(row) } : { error: "missing_portal_context", project, portal, hint: "Call mem_portal_context_set with company/brand/domain/language/design/credits/prices/rights and all three rule layers." };
|
|
3210
|
-
}
|
|
3211
|
-
|
|
3212
|
-
function portalContextList(db, input = {}) {
|
|
3213
|
-
ensureAgentGovernanceSchema(db);
|
|
3214
|
-
const scope = scopeName(input.scope);
|
|
3215
|
-
const where = ["scope=?"];
|
|
3216
|
-
const params = [scope];
|
|
3217
|
-
if (input.project) { where.push("project=?"); params.push(String(input.project)); }
|
|
3218
|
-
if (input.status) { where.push("status=?"); params.push(String(input.status)); }
|
|
3219
|
-
else if (!input.include_inactive) where.push("status='active'");
|
|
3220
|
-
const rows = db.prepare("SELECT * FROM portal_context WHERE " + where.join(" AND ") + " ORDER BY updated_at DESC LIMIT ?").all(...params, clampInt(input.limit, 100, 1, 1000)).map(rowToPortalContext);
|
|
3221
|
-
return { ok: true, count: rows.length, contexts: rows };
|
|
3222
|
-
}
|
|
3223
|
-
|
|
3224
|
-
function activeClaimsForAgent(db, input = {}) {
|
|
3225
|
-
if (!tableExists(db, "work_claim") || !input.agent_name) return [];
|
|
3226
|
-
const params = [input.agent_name];
|
|
3227
|
-
const where = ["agent_name=?", "status IN ('active','stale')"];
|
|
3228
|
-
if (input.project) { where.push("project=?"); params.push(input.project); }
|
|
3229
|
-
return db.prepare("SELECT id, project, file_path, claim_kind, scope_value, scope_key, summary, status, expires_at, heartbeat_at FROM work_claim WHERE " + where.join(" AND ") + " ORDER BY heartbeat_at DESC, id DESC LIMIT 100").all(...params);
|
|
3230
|
-
}
|
|
3231
|
-
|
|
3232
|
-
function activeClaimsForScopeKeys(db, input = {}) {
|
|
3233
|
-
if (!tableExists(db, "work_claim")) return [];
|
|
3234
|
-
const keys = Array.isArray(input.scope_keys) ? input.scope_keys.filter(Boolean) : [];
|
|
3235
|
-
if (!keys.length) return [];
|
|
3236
|
-
const where = ["status IN ('active','stale')", "scope_key IN (" + keys.map(() => "?").join(",") + ")"];
|
|
3237
|
-
const params = keys.slice();
|
|
3238
|
-
if (input.project) { where.push("project=?"); params.push(input.project); }
|
|
3239
|
-
return db.prepare("SELECT id, project, file_path, agent_name, claim_kind, scope_value, scope_key, summary, status, expires_at, heartbeat_at FROM work_claim WHERE " + where.join(" AND ") + " ORDER BY heartbeat_at DESC, id DESC LIMIT 100").all(...params);
|
|
3240
|
-
}
|
|
3241
|
-
|
|
3242
|
-
function recentWorkflowReceipts(db, input = {}) {
|
|
3243
|
-
if (!tableExists(db, "agent_workflow_receipt")) return [];
|
|
3244
|
-
const where = ["scope=?"];
|
|
3245
|
-
const params = [scopeName(input.scope)];
|
|
3246
|
-
if (input.project) { where.push("project=?"); params.push(input.project); }
|
|
3247
|
-
if (input.agent_name) { where.push("agent_name=?"); params.push(normalizeAgentName(input.agent_name)); }
|
|
3248
|
-
if (input.portal) { where.push("(portal=? OR portal IS NULL OR portal='')"); params.push(portalName(input)); }
|
|
3249
|
-
return db.prepare("SELECT id, project, portal, agent_name, role_name, phase, summary, status, created_at FROM agent_workflow_receipt WHERE " + where.join(" AND ") + " ORDER BY created_at DESC, id DESC LIMIT ?")
|
|
3250
|
-
.all(...params, clampInt(input.limit, 20, 1, 100));
|
|
3251
|
-
}
|
|
3252
|
-
|
|
3253
|
-
function agentCompanyPreflight(db, input = {}) {
|
|
3254
|
-
if (!input.agent_name || !input.task) return { error: "agent_name + task required" };
|
|
3255
|
-
ensureAgentGovernanceSchema(db);
|
|
3256
|
-
const checks = [];
|
|
3257
|
-
const blockers = [];
|
|
3258
|
-
const warnings = [];
|
|
3259
|
-
const project = textOrNull(input.project, 500);
|
|
3260
|
-
const portal = portalName(input);
|
|
3261
|
-
const agent = normalizeAgentName(input.agent_name);
|
|
3262
|
-
const requestedRole = input.role || input.role_name;
|
|
3263
|
-
const assignment = requestedRole ? { role_name: canonicalRoleName(requestedRole), source: "input" } : latestRoleAssignment(db, { scope: input.scope, agent_name: agent, project, portal });
|
|
3264
|
-
if (!assignment || !assignment.role_name) { checks.push({ name: "role_selection", result: "block" }); blockers.push("missing gstack/Mnemo role selection"); }
|
|
3265
|
-
else if (!gstackRole(assignment.role_name)) { checks.push({ name: "role_selection", result: "block", role: assignment.role_name }); blockers.push("unknown role: " + assignment.role_name); }
|
|
3266
|
-
else checks.push({ name: "role_selection", result: "ok", role: assignment.role_name, source: assignment.source || "assignment" });
|
|
3267
|
-
|
|
3268
|
-
let portalContext = null;
|
|
3269
|
-
const requirePortal = input.require_portal_context === true || (!!project && input.require_portal_context !== false);
|
|
3270
|
-
if (project && requirePortal) {
|
|
3271
|
-
const contextResult = portalContextGet(db, { scope: input.scope, project, portal });
|
|
3272
|
-
portalContext = contextResult.context || null;
|
|
3273
|
-
if (!portalContext) { checks.push({ name: "portal_context", result: "block", portal }); blockers.push("missing portal context for " + project + ":" + portal); }
|
|
3274
|
-
else {
|
|
3275
|
-
const missingFields = [];
|
|
3276
|
-
for (const field of [
|
|
3277
|
-
"portal_id",
|
|
3278
|
-
"portal_name",
|
|
3279
|
-
"brand_name",
|
|
3280
|
-
"domain",
|
|
3281
|
-
"environment",
|
|
3282
|
-
"country_or_market",
|
|
3283
|
-
"user_role",
|
|
3284
|
-
"language",
|
|
3285
|
-
"supported_languages",
|
|
3286
|
-
"design",
|
|
3287
|
-
"credit_system",
|
|
3288
|
-
"pricing",
|
|
3289
|
-
"rights",
|
|
3290
|
-
"billing_owner",
|
|
3291
|
-
"auth_owner",
|
|
3292
|
-
"deployment_owner",
|
|
3293
|
-
"legal_owner",
|
|
3294
|
-
"forbidden_cross_portal_leaks",
|
|
3295
|
-
"shared_modules",
|
|
3296
|
-
"protected_surfaces"
|
|
3297
|
-
]) if (!hasValue(portalContext[field])) missingFields.push(field);
|
|
3298
|
-
const missingLayers = [];
|
|
3299
|
-
for (const layer of ["global_rules", "portal_rules", "customer_partner_rules"]) if (!hasValue(portalContext[layer])) missingLayers.push(layer);
|
|
3300
|
-
checks.push({ name: "portal_context", result: missingFields.length || missingLayers.length ? "block" : "ok", portal, missing_fields: missingFields, missing_rule_layers: missingLayers });
|
|
3301
|
-
if (missingFields.length) blockers.push("portal context missing fields: " + missingFields.join(", "));
|
|
3302
|
-
if (missingLayers.length) blockers.push("portal context missing rule layers: " + missingLayers.join(", "));
|
|
3303
|
-
}
|
|
3304
|
-
}
|
|
3305
|
-
const claims = activeClaimsForAgent(db, { agent_name: agent, project });
|
|
3306
|
-
const files = Array.isArray(input.files) ? input.files : [];
|
|
3307
|
-
const missingClaimKeys = files.map((file) => claimScopeKey("file", file)).filter(Boolean).filter((key) => !claims.some((claim) => String(claim.scope_key || "") === key));
|
|
3308
|
-
const conflictingClaims = activeClaimsForScopeKeys(db, { project, scope_keys: files.map((file) => claimScopeKey("file", file)).filter(Boolean) })
|
|
3309
|
-
.filter((claim) => normalizeAgentName(claim.agent_name) !== agent);
|
|
3310
|
-
checks.push({ name: "work_claim", result: missingClaimKeys.length || conflictingClaims.length ? "block" : "ok", active_claims: claims.length, missing_claims: missingClaimKeys, conflicting_claims: conflictingClaims });
|
|
3311
|
-
if (missingClaimKeys.length && input.require_work_claim !== false) blockers.push("missing active work claims: " + missingClaimKeys.join(", "));
|
|
3312
|
-
if (conflictingClaims.length) blockers.push("scope already claimed by another agent: " + conflictingClaims.map((claim) => `${claim.agent_name}:${claim.scope_key}`).join(", "));
|
|
3313
|
-
const hasPlan = !!textOrNull(input.plan || input.plan_summary, 8000) || !!(assignment && assignment.plan_summary);
|
|
3314
|
-
checks.push({ name: "plan_receipt", result: hasPlan ? "ok" : "block" });
|
|
3315
|
-
if (!hasPlan && input.require_plan !== false) blockers.push("missing saved plan/plan_summary");
|
|
3316
|
-
const deployPrecheckGaps = deployPrecheckInputGaps(input);
|
|
3317
|
-
if (isDeployAction(input)) {
|
|
3318
|
-
checks.push({
|
|
3319
|
-
name: "deploy_precheck",
|
|
3320
|
-
result: deployPrecheckGaps.length ? "block" : "ok",
|
|
3321
|
-
required_evidence: DEPLOY_PRECHECK_REQUIRED_EVIDENCE,
|
|
3322
|
-
missing: deployPrecheckGaps,
|
|
3323
|
-
});
|
|
3324
|
-
if (deployPrecheckGaps.length) {
|
|
3325
|
-
blockers.push("deploy precheck required before live deploy: " + deployPrecheckGaps.join(", "));
|
|
3326
|
-
}
|
|
3327
|
-
}
|
|
3328
|
-
if (Array.isArray(input.dependencies) && input.dependencies.length) checks.push({ name: "dependencies", result: "ok", dependencies: input.dependencies });
|
|
3329
|
-
else warnings.push("dependencies not provided");
|
|
3330
|
-
const status = blockers.length ? "block" : "ok";
|
|
3331
|
-
try {
|
|
3332
|
-
db.prepare("INSERT INTO agent_action (agent_name, action_kind, target, status, payload_json, topic) VALUES (?, 'company_preflight', ?, ?, ?, 'agent_governance')")
|
|
3333
|
-
.run(agent, project || input.task, status, JSON.stringify({ task: input.task, role: assignment && assignment.role_name || null, portal, blockers, checks, warnings }));
|
|
3334
|
-
} catch {}
|
|
3335
|
-
return { status, ok: status === "ok", agent_name: agent, project, portal, role: assignment && assignment.role_name || null, workflow: GSTACK_WORKFLOW, blockers, warnings, checks, portal_context: portalContext, active_claims: claims, hint: status === "ok" ? "Proceed through build/review/test/ship/reflect/memorize with receipts and handoff." : "Resolve blockers before changing code, text, prices, design, or logic." };
|
|
3336
|
-
}
|
|
3337
|
-
|
|
3338
|
-
function workflowReceiptCreate(db, input = {}) {
|
|
3339
|
-
if (!input.agent_name || !input.phase || !input.summary) return { error: "agent_name + phase + summary required" };
|
|
3340
|
-
ensureAgentGovernanceSchema(db);
|
|
3341
|
-
const phase = roleKey(input.phase);
|
|
3342
|
-
if (!GSTACK_WORKFLOW.includes(phase) && !["decision", "risk", "evidence", "handoff"].includes(phase)) return { error: "invalid_phase", allowed: GSTACK_WORKFLOW };
|
|
3343
|
-
const agent = normalizeAgentName(input.agent_name);
|
|
3344
|
-
const role = input.role || input.role_name || (latestRoleAssignment(db, { scope: input.scope, agent_name: agent, project: input.project, portal: input.portal }) || {}).role_name || null;
|
|
3345
|
-
const info = db.prepare("INSERT INTO agent_workflow_receipt (scope, project, portal, agent_name, role_name, phase, summary, evidence_json, risks_json, affected_portals_json, status, meta_json) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)")
|
|
3346
|
-
.run(scopeName(input.scope), input.project || null, input.portal ? portalName(input) : null, agent, role, phase, textOrNull(input.summary, 12000), safeJson(input.evidence || [], []), safeJson(input.risks || [], []), safeJson(input.affected_portals || [], []), input.status || "done", safeJson(input.meta || {}, {}));
|
|
3347
|
-
try {
|
|
3348
|
-
db.prepare("INSERT INTO agent_action (agent_name, action_kind, target, status, payload_json, topic) VALUES (?, 'workflow_receipt', ?, 'done', ?, 'agent_governance')")
|
|
3349
|
-
.run(agent, input.project || phase, JSON.stringify({ receipt_id: info.lastInsertRowid, phase, role }));
|
|
3350
|
-
} catch {}
|
|
3351
|
-
return { ok: true, id: info.lastInsertRowid, phase, role };
|
|
3352
|
-
}
|
|
3353
|
-
|
|
3354
|
-
const PROJECT_RULE_JSON_KEYS = ["canonical_nav", "allowed_domains", "auth_matrix", "language_matrix", "pricing_rules", "checkout_rules", "vat_rules", "deploy_rules", "design_rules", "required_gates"];
|
|
3355
|
-
|
|
3356
|
-
function stableValue(value) {
|
|
3357
|
-
if (Array.isArray(value)) return value.map(stableValue);
|
|
3358
|
-
if (value && typeof value === "object") {
|
|
3359
|
-
return Object.keys(value).sort().reduce((out, key) => {
|
|
3360
|
-
out[key] = stableValue(value[key]);
|
|
3361
|
-
return out;
|
|
3362
|
-
}, {});
|
|
3363
|
-
}
|
|
3364
|
-
return value;
|
|
3365
|
-
}
|
|
3366
|
-
|
|
3367
|
-
function stableJson(value) {
|
|
3368
|
-
return JSON.stringify(stableValue(value));
|
|
3369
|
-
}
|
|
3370
|
-
|
|
3371
|
-
function loadProjectRuleBundle(db, project) {
|
|
3372
|
-
if (!project || !tableExists(db, "project_rules")) return null;
|
|
3373
|
-
const row = db.prepare("SELECT * FROM project_rules WHERE project=?").get(project);
|
|
3374
|
-
if (!row) return null;
|
|
3375
|
-
const out = { project: row.project, notes: row.notes || null, updated_by: row.updated_by || null, updated_at: row.updated_at || null };
|
|
3376
|
-
for (const key of PROJECT_RULE_JSON_KEYS) out[key] = parseJson(row[key], null);
|
|
3377
|
-
return out;
|
|
3378
|
-
}
|
|
3379
|
-
|
|
3380
|
-
function flattenRuleBundle(value, prefix = "", out = {}) {
|
|
3381
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
3382
|
-
for (const key of Object.keys(value).sort()) {
|
|
3383
|
-
flattenRuleBundle(value[key], prefix ? prefix + "." + key : key, out);
|
|
3384
|
-
}
|
|
3385
|
-
return out;
|
|
3386
|
-
}
|
|
3387
|
-
out[prefix || "value"] = stableJson(value);
|
|
3388
|
-
return out;
|
|
3389
|
-
}
|
|
3390
|
-
|
|
3391
|
-
function diffRuleBundles(before, after) {
|
|
3392
|
-
const left = flattenRuleBundle(before || {});
|
|
3393
|
-
const right = flattenRuleBundle(after || {});
|
|
3394
|
-
const keys = Array.from(new Set(Object.keys(left).concat(Object.keys(right)))).sort();
|
|
3395
|
-
const added = [];
|
|
3396
|
-
const removed = [];
|
|
3397
|
-
const changed = [];
|
|
3398
|
-
for (const key of keys) {
|
|
3399
|
-
if (!(key in left)) added.push({ path: key, value: parseJson(right[key], right[key]) });
|
|
3400
|
-
else if (!(key in right)) removed.push({ path: key, value: parseJson(left[key], left[key]) });
|
|
3401
|
-
else if (left[key] !== right[key]) changed.push({ path: key, before: parseJson(left[key], left[key]), after: parseJson(right[key], right[key]) });
|
|
3402
|
-
}
|
|
3403
|
-
return { added, removed, changed, changed_count: added.length + removed.length + changed.length };
|
|
3404
|
-
}
|
|
3405
|
-
|
|
3406
|
-
function ownerRuleDiff(db, input = {}) {
|
|
3407
|
-
const project = normalizeProject(input.project || input.name);
|
|
3408
|
-
if (!project) return { error: "project required" };
|
|
3409
|
-
ensureAgentGovernanceSchema(db);
|
|
3410
|
-
const current = loadProjectRuleBundle(db, project);
|
|
3411
|
-
if (!current) return { error: "project_rules_not_found", project };
|
|
3412
|
-
const scope = scopeName(input.scope);
|
|
3413
|
-
let previous = null;
|
|
3414
|
-
if (input.before_snapshot_id || input.snapshot_id) {
|
|
3415
|
-
previous = db.prepare("SELECT * FROM owner_rule_snapshot WHERE id=?").get(parseInt(input.before_snapshot_id || input.snapshot_id, 10));
|
|
3416
|
-
} else {
|
|
3417
|
-
previous = db.prepare("SELECT * FROM owner_rule_snapshot WHERE scope=? AND project=? ORDER BY created_at DESC, id DESC LIMIT 1").get(scope, project);
|
|
3418
|
-
}
|
|
3419
|
-
const currentHash = sha(stableJson(current));
|
|
3420
|
-
const previousRules = previous ? parseJson(previous.rules_json, {}) : null;
|
|
3421
|
-
const diff = previousRules ? diffRuleBundles(previousRules, current) : { added: [], removed: [], changed: [], changed_count: 0 };
|
|
3422
|
-
let snapshotId = null;
|
|
3423
|
-
if (input.create_snapshot !== false) {
|
|
3424
|
-
const info = db.prepare("INSERT INTO owner_rule_snapshot (scope, project, rules_hash, rules_json, created_by, meta_json) VALUES (?,?,?,?,?,?)")
|
|
3425
|
-
.run(scope, project, currentHash, stableJson(current), input.agent_name || input.created_by || input.updated_by || "unknown", safeJson(Object.assign({}, input.meta || {}, { previous_snapshot_id: previous && previous.id || null, previous_hash: previous && previous.rules_hash || null }), {}));
|
|
3426
|
-
snapshotId = info.lastInsertRowid;
|
|
3427
|
-
}
|
|
3428
|
-
return { ok: true, project, current_hash: currentHash, previous_snapshot_id: previous && previous.id || null, previous_hash: previous && previous.rules_hash || null, snapshot_id: snapshotId, diff };
|
|
3429
|
-
}
|
|
3430
|
-
|
|
3431
|
-
function normalizedTaskFingerprintText(input = {}) {
|
|
3432
|
-
const parts = [
|
|
3433
|
-
input.summary,
|
|
3434
|
-
input.task,
|
|
3435
|
-
input.title,
|
|
3436
|
-
input.objective,
|
|
3437
|
-
input.customer_request,
|
|
3438
|
-
input.symptoms,
|
|
3439
|
-
input.message_ref,
|
|
3440
|
-
input.source_ref,
|
|
3441
|
-
input.project,
|
|
3442
|
-
input.portal
|
|
3443
|
-
];
|
|
3444
|
-
for (const key of ["files", "routes", "urls", "modules", "tags"]) {
|
|
3445
|
-
if (Array.isArray(input[key])) parts.push(...input[key]);
|
|
3446
|
-
else if (input[key]) parts.push(input[key]);
|
|
3447
|
-
}
|
|
3448
|
-
return normalizeEvidenceText(parts.filter(Boolean).join(" "));
|
|
3449
|
-
}
|
|
3450
|
-
|
|
3451
|
-
function tokenSet(text) {
|
|
3452
|
-
return new Set(String(text || "").split(/\s+/).filter((token) => token.length > 3));
|
|
3453
|
-
}
|
|
3454
|
-
|
|
3455
|
-
function tokenSimilarity(a, b) {
|
|
3456
|
-
const left = tokenSet(a);
|
|
3457
|
-
const right = tokenSet(b);
|
|
3458
|
-
if (!left.size || !right.size) return 0;
|
|
3459
|
-
let common = 0;
|
|
3460
|
-
for (const token of left) if (right.has(token)) common++;
|
|
3461
|
-
return common / Math.max(left.size, right.size);
|
|
3462
|
-
}
|
|
3463
|
-
|
|
3464
|
-
function taskFingerprint(db, input = {}) {
|
|
3465
|
-
ensureAgentGovernanceSchema(db);
|
|
3466
|
-
const normalized = normalizedTaskFingerprintText(input);
|
|
3467
|
-
if (!normalized) return { error: "summary/task/title required" };
|
|
3468
|
-
const scope = scopeName(input.scope);
|
|
3469
|
-
const project = normalizeProject(input.project || input.name) || null;
|
|
3470
|
-
const portal = input.portal ? portalName(input) : null;
|
|
3471
|
-
const fingerprint = sha(normalized).slice(0, 32);
|
|
3472
|
-
const where = ["scope=?", "status='active'"];
|
|
3473
|
-
const params = [scope];
|
|
3474
|
-
if (project) { where.push("(project=? OR project IS NULL)"); params.push(project); }
|
|
3475
|
-
const rows = db.prepare("SELECT * FROM task_fingerprint WHERE " + where.join(" AND ") + " ORDER BY updated_at DESC LIMIT 200").all(...params);
|
|
3476
|
-
const exact = rows.filter((row) => row.fingerprint === fingerprint);
|
|
3477
|
-
const similar = rows
|
|
3478
|
-
.map((row) => Object.assign({}, row, { similarity: tokenSimilarity(normalized, row.normalized_text) }))
|
|
3479
|
-
.filter((row) => row.similarity >= (Number(input.similarity_threshold) || 0.65))
|
|
3480
|
-
.sort((a, b) => b.similarity - a.similarity)
|
|
3481
|
-
.slice(0, 10);
|
|
3482
|
-
let id = null;
|
|
3483
|
-
if (input.persist !== false) {
|
|
3484
|
-
const info = db.prepare("INSERT INTO task_fingerprint (scope, project, portal, fingerprint, summary, normalized_text, agent_name, source_kind, source_id, status, linked_task_id, meta_json) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)")
|
|
3485
|
-
.run(scope, project, portal, fingerprint, textOrNull(input.summary || input.task || input.title || normalized, 2000), normalized, input.agent_name || null, input.source_kind || null, input.source_id || input.message_ref || null, input.status || "active", input.linked_task_id || input.task_id || null, safeJson(input.meta || {}, {}));
|
|
3486
|
-
id = info.lastInsertRowid;
|
|
3487
|
-
}
|
|
3488
|
-
return { ok: true, id, fingerprint, duplicate: exact.length > 0, exact_matches: exact, similar_matches: similar };
|
|
3489
|
-
}
|
|
3490
|
-
|
|
3491
|
-
function ruleViolationLog(db, input = {}) {
|
|
3492
|
-
if (!input.agent_name || !input.rule_key) return { error: "agent_name + rule_key required" };
|
|
3493
|
-
ensureAgentGovernanceSchema(db);
|
|
3494
|
-
const info = db.prepare("INSERT INTO rule_violation_log (scope, project, portal, agent_name, rule_key, rule_text, action_kind, target, severity, status, evidence_json, prevention_json, meta_json) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)")
|
|
3495
|
-
.run(
|
|
3496
|
-
scopeName(input.scope),
|
|
3497
|
-
normalizeProject(input.project || input.name) || null,
|
|
3498
|
-
input.portal ? portalName(input) : null,
|
|
3499
|
-
normalizeAgentName(input.agent_name),
|
|
3500
|
-
textOrNull(input.rule_key, 300) || "unknown",
|
|
3501
|
-
textOrNull(input.rule_text || input.rule || input.summary, 4000),
|
|
3502
|
-
textOrNull(input.action_kind || input.action, 300),
|
|
3503
|
-
textOrNull(input.target || input.file_path || input.route || input.system_name, 1000),
|
|
3504
|
-
textOrNull(input.severity, 20) || "M",
|
|
3505
|
-
textOrNull(input.status, 50) || "open",
|
|
3506
|
-
safeJson(input.evidence || [], []),
|
|
3507
|
-
safeJson(input.prevention || input.prevention_rule || {}, {}),
|
|
3508
|
-
safeJson(input.meta || {}, {})
|
|
3509
|
-
);
|
|
3510
|
-
return { ok: true, id: info.lastInsertRowid };
|
|
3511
|
-
}
|
|
3512
|
-
|
|
3513
|
-
function neverAgainCheck(db, input = {}) {
|
|
3514
|
-
ensureAgentGovernanceSchema(db);
|
|
3515
|
-
const project = normalizeProject(input.project || input.name) || null;
|
|
3516
|
-
const scope = scopeName(input.scope);
|
|
3517
|
-
const summary = textOrNull(input.summary || input.task || input.action || input.title, 4000);
|
|
3518
|
-
if (!summary && !project) return { error: "summary_or_project required" };
|
|
3519
|
-
const blockers = [];
|
|
3520
|
-
const warnings = [];
|
|
3521
|
-
const matches = { rule_violations: [], ops_incidents: [], task_duplicates: [] };
|
|
3522
|
-
if (tableExists(db, "rule_violation_log")) {
|
|
3523
|
-
const where = ["scope=?", "status IN ('open','active')"];
|
|
3524
|
-
const params = [scope];
|
|
3525
|
-
if (project) { where.push("(project=? OR project IS NULL)"); params.push(project); }
|
|
3526
|
-
matches.rule_violations = db.prepare("SELECT id, project, portal, agent_name, rule_key, rule_text, action_kind, target, severity, status, created_at FROM rule_violation_log WHERE " + where.join(" AND ") + " ORDER BY created_at DESC LIMIT 50").all(...params);
|
|
3527
|
-
for (const row of matches.rule_violations) {
|
|
3528
|
-
if (String(row.severity || "").toUpperCase() === "H" || String(row.severity || "").toLowerCase() === "critical") blockers.push("open high-severity rule violation: #" + row.id + " " + row.rule_key);
|
|
3529
|
-
}
|
|
3530
|
-
}
|
|
3531
|
-
if (tableExists(db, "ops_incident")) {
|
|
3532
|
-
const where = ["status='open'"];
|
|
3533
|
-
const params = [];
|
|
3534
|
-
if (project) { where.push("(project=? OR project IS NULL)"); params.push(project); }
|
|
3535
|
-
matches.ops_incidents = db.prepare("SELECT id, project, system_name, title, severity, cause, prevention, opened_at FROM ops_incident WHERE " + where.join(" AND ") + " ORDER BY opened_at DESC LIMIT 25").all(...params);
|
|
3536
|
-
for (const row of matches.ops_incidents) {
|
|
3537
|
-
if (String(row.severity || "").toUpperCase() === "H") blockers.push("open high-severity incident: #" + row.id + " " + row.title);
|
|
3538
|
-
else warnings.push("open incident to consider: #" + row.id + " " + row.title);
|
|
3539
|
-
}
|
|
3540
|
-
}
|
|
3541
|
-
if (summary) {
|
|
3542
|
-
const fp = taskFingerprint(db, Object.assign({}, input, { persist: false, project, scope }));
|
|
3543
|
-
matches.task_duplicates = (fp.exact_matches || []).concat(fp.similar_matches || []);
|
|
3544
|
-
if (fp.duplicate) blockers.push("duplicate task fingerprint already exists: " + fp.fingerprint);
|
|
3545
|
-
else if ((fp.similar_matches || []).length) warnings.push("similar task fingerprint exists: " + fp.similar_matches[0].id);
|
|
3546
|
-
}
|
|
3547
|
-
const projectRules = project ? loadProjectRuleBundle(db, project) : null;
|
|
3548
|
-
if (project && !projectRules) warnings.push("project rules not found for never-again check");
|
|
3549
|
-
const status = blockers.length ? "block" : "ok";
|
|
3550
|
-
return { status, ok: status === "ok", project, blockers, warnings, matches, project_rules_loaded: !!projectRules };
|
|
3551
|
-
}
|
|
3552
|
-
|
|
3553
|
-
function agentBlameReport(db, input = {}) {
|
|
3554
|
-
ensureAgentGovernanceSchema(db);
|
|
3555
|
-
const agent = input.agent_name ? normalizeAgentName(input.agent_name) : null;
|
|
3556
|
-
const project = normalizeProject(input.project || input.name) || null;
|
|
3557
|
-
const days = clampInt(input.days, 7, 1, 365);
|
|
3558
|
-
const since = new Date(Date.now() - days * 86400000).toISOString();
|
|
3559
|
-
const report = { ok: true, agent_name: agent, project, days, since, counts: {}, samples: {}, reliability: {} };
|
|
3560
|
-
if (tableExists(db, "agent_action")) {
|
|
3561
|
-
const where = ["started_at>=?"];
|
|
3562
|
-
const params = [since];
|
|
3563
|
-
if (agent) { where.push("agent_name=?"); params.push(agent); }
|
|
3564
|
-
if (project) { where.push("(target LIKE ? OR topic LIKE ?)"); params.push("%" + project + "%", "%" + project + "%"); }
|
|
3565
|
-
report.counts.actions_by_status = db.prepare("SELECT status, COUNT(*) count FROM agent_action WHERE " + where.join(" AND ") + " GROUP BY status").all(...params);
|
|
3566
|
-
report.samples.failed_actions = db.prepare("SELECT id, agent_name, action_kind, target, status, started_at FROM agent_action WHERE " + where.join(" AND ") + " AND status NOT IN ('done','ok','success','captured') ORDER BY started_at DESC LIMIT 20").all(...params);
|
|
3567
|
-
}
|
|
3568
|
-
if (tableExists(db, "rule_violation_log")) {
|
|
3569
|
-
const where = ["created_at>=?"];
|
|
3570
|
-
const params = [since];
|
|
3571
|
-
if (agent) { where.push("agent_name=?"); params.push(agent); }
|
|
3572
|
-
if (project) { where.push("(project=? OR project IS NULL)"); params.push(project); }
|
|
3573
|
-
report.counts.rule_violations = db.prepare("SELECT severity, status, COUNT(*) count FROM rule_violation_log WHERE " + where.join(" AND ") + " GROUP BY severity, status").all(...params);
|
|
3574
|
-
report.samples.rule_violations = db.prepare("SELECT id, agent_name, rule_key, severity, status, created_at FROM rule_violation_log WHERE " + where.join(" AND ") + " ORDER BY created_at DESC LIMIT 20").all(...params);
|
|
3575
|
-
}
|
|
3576
|
-
if (tableExists(db, "agent_workflow_receipt")) {
|
|
3577
|
-
const where = ["created_at>=?"];
|
|
3578
|
-
const params = [since];
|
|
3579
|
-
if (agent) { where.push("agent_name=?"); params.push(agent); }
|
|
3580
|
-
if (project) { where.push("(project=? OR project IS NULL)"); params.push(project); }
|
|
3581
|
-
report.counts.workflow_receipts = db.prepare("SELECT phase, status, COUNT(*) count FROM agent_workflow_receipt WHERE " + where.join(" AND ") + " GROUP BY phase, status").all(...params);
|
|
3582
|
-
}
|
|
3583
|
-
if (tableExists(db, "session_handoff")) {
|
|
3584
|
-
const where = ["created_at>=?"];
|
|
3585
|
-
const params = [since];
|
|
3586
|
-
if (agent) { where.push("agent_name=?"); params.push(agent); }
|
|
3587
|
-
if (project) { where.push("(project=? OR project IS NULL)"); params.push(project); }
|
|
3588
|
-
report.counts.handoffs = db.prepare("SELECT COUNT(*) count FROM session_handoff WHERE " + where.join(" AND ")).get(...params).count;
|
|
3589
|
-
}
|
|
3590
|
-
const failures = (report.samples.failed_actions || []).length;
|
|
3591
|
-
const violations = (report.samples.rule_violations || []).length;
|
|
3592
|
-
const receipts = (report.counts.workflow_receipts || []).reduce((sum, row) => sum + row.count, 0);
|
|
3593
|
-
const handoffs = Number(report.counts.handoffs || 0);
|
|
3594
|
-
const score = Math.max(0, Math.min(100, 80 + Math.min(10, receipts) + Math.min(10, handoffs * 2) - failures * 3 - violations * 8));
|
|
3595
|
-
report.reliability = { score, band: score >= 90 ? "high" : score >= 70 ? "normal" : "risk", factors: { failures, violations, receipts, handoffs } };
|
|
3596
|
-
return report;
|
|
3597
|
-
}
|
|
3598
|
-
|
|
3599
|
-
function completionGuardCheck(db, input = {}) {
|
|
3600
|
-
ensureAgentGovernanceSchema(db);
|
|
3601
|
-
const evidence = Array.isArray(input.evidence) ? input.evidence : [];
|
|
3602
|
-
const workOrder = input.work_order_id && tableExists(db, "work_order")
|
|
3603
|
-
? rowToWorkOrder(db.prepare("SELECT * FROM work_order WHERE id=?").get(parseInt(input.work_order_id, 10)))
|
|
3604
|
-
: null;
|
|
3605
|
-
const workOrderRequired = workOrder ? requiredEvidenceForOrder(workOrder, input) : [];
|
|
3606
|
-
const required = uniqueStrings(listInput(input.required_evidence || input.requirements).concat(workOrderRequired));
|
|
3607
|
-
const invalid = evidence.map(validateEvidenceItem).filter(Boolean).concat(evidence.map(nonPassEvidenceReason).filter(Boolean));
|
|
3608
|
-
const missing = required.filter((requirement) => !evidence.some((item) => evidenceMatchesRequirement(item, requirement)));
|
|
3609
|
-
const blockers = [];
|
|
3610
|
-
if (!evidence.length && input.require_evidence !== false) blockers.push("evidence required");
|
|
3611
|
-
if (invalid.length) blockers.push("invalid/non-passing evidence");
|
|
3612
|
-
if (missing.length) blockers.push("missing required evidence: " + missing.join(", "));
|
|
3613
|
-
const tests = Array.isArray(input.tests) ? input.tests : [];
|
|
3614
|
-
const hasTestEvidence = tests.length > 0 || evidence.some((item) => /\b(test|check|smoke|verify|qa|integrity|npm|unit)\b/i.test(evidenceText(item)));
|
|
3615
|
-
if (!hasTestEvidence && input.require_tests !== false) blockers.push("tests/checks not provided");
|
|
3616
|
-
const handoffId = input.handoff_id || input.session_handoff_id;
|
|
3617
|
-
if (!handoffId && input.require_handoff !== false) blockers.push("handoff required");
|
|
3618
|
-
const openBlockers = listInput(input.blockers || input.open_blockers);
|
|
3619
|
-
if (openBlockers.length) blockers.push("unfinished blockers: " + openBlockers.join(", "));
|
|
3620
|
-
const approvalCheck = approvalRefsCheck(db, {
|
|
3621
|
-
approval_ids: uniqueStrings(
|
|
3622
|
-
approvalIdList(input.approval_ids)
|
|
3623
|
-
.concat(approvalIdList(input.approval_id))
|
|
3624
|
-
.concat(workOrder ? (workOrder.approval_ids || []).map(String) : [])
|
|
3625
|
-
),
|
|
3626
|
-
require_approval: input.require_approval === true || input.require_approvals === true,
|
|
3627
|
-
});
|
|
3628
|
-
if (!approvalCheck.ok) blockers.push(...approvalCheck.blockers.map((entry) => "approval " + entry));
|
|
3629
|
-
const never = input.skip_never_again === true ? null : neverAgainCheck(db, { scope: input.scope, project: input.project, task: input.summary || input.task || input.title });
|
|
3630
|
-
if (never && never.status === "block") blockers.push(...never.blockers.map((item) => "never-again: " + item));
|
|
3631
|
-
const status = blockers.length ? "block" : "pass";
|
|
3632
|
-
return { status, ok: status === "pass", blockers, invalid_evidence: invalid, missing_required: missing, required_evidence: required, tests_checked: hasTestEvidence, handoff_id: handoffId || null, approval_check: approvalCheck, never_again: never };
|
|
3633
|
-
}
|
|
3634
|
-
|
|
3635
|
-
function agentOsBoot(db, input = {}) {
|
|
3636
|
-
if (!input.agent_name || !input.task) return { error: "agent_name + task required" };
|
|
3637
|
-
ensureAgentGovernanceSchema(db);
|
|
3638
|
-
const agent = normalizeAgentName(input.agent_name);
|
|
3639
|
-
const project = textOrNull(input.project, 500);
|
|
3640
|
-
const portal = portalName(input);
|
|
3641
|
-
let roleAssignment = null;
|
|
3642
|
-
let roleContract = null;
|
|
3643
|
-
if (input.role || input.role_name) {
|
|
3644
|
-
const selected = agentRoleSelect(db, {
|
|
3645
|
-
scope: input.scope,
|
|
3646
|
-
agent_name: agent,
|
|
3647
|
-
role: input.role || input.role_name,
|
|
3648
|
-
project,
|
|
3649
|
-
portal,
|
|
3650
|
-
task: input.task,
|
|
3651
|
-
plan: input.plan || input.plan_summary,
|
|
3652
|
-
replace_active: input.replace_active,
|
|
3653
|
-
selected_by: input.selected_by || agent,
|
|
3654
|
-
meta: Object.assign({}, input.meta || {}, { source: "mem_agent_os_boot" })
|
|
3655
|
-
});
|
|
3656
|
-
if (selected.error) return selected;
|
|
3657
|
-
roleAssignment = selected.assignment;
|
|
3658
|
-
roleContract = selected.role_contract;
|
|
3659
|
-
} else {
|
|
3660
|
-
const existing = agentRoleGet(db, { scope: input.scope, agent_name: agent, project, portal });
|
|
3661
|
-
roleAssignment = existing.assignment || null;
|
|
3662
|
-
roleContract = existing.role_contract || null;
|
|
3663
|
-
}
|
|
3664
|
-
const preflight = agentCompanyPreflight(db, {
|
|
3665
|
-
scope: input.scope,
|
|
3666
|
-
agent_name: agent,
|
|
3667
|
-
role: roleAssignment && roleAssignment.role_name || input.role || input.role_name,
|
|
3668
|
-
project,
|
|
3669
|
-
portal,
|
|
3670
|
-
task: input.task,
|
|
3671
|
-
plan: input.plan || input.plan_summary,
|
|
3672
|
-
files: Array.isArray(input.files) ? input.files : [],
|
|
3673
|
-
dependencies: Array.isArray(input.dependencies) ? input.dependencies : [],
|
|
3674
|
-
require_portal_context: input.require_portal_context,
|
|
3675
|
-
require_work_claim: input.require_work_claim,
|
|
3676
|
-
require_plan: input.require_plan
|
|
3677
|
-
});
|
|
3678
|
-
const board = agentCompanyBoard(db, { scope: input.scope, project, scale_target_agents: input.scale_target_agents || 100 });
|
|
3679
|
-
const receipts = recentWorkflowReceipts(db, { scope: input.scope, project, portal, agent_name: agent, limit: input.receipt_limit || 20 });
|
|
3680
|
-
return {
|
|
3681
|
-
ok: preflight.status === "ok",
|
|
3682
|
-
status: preflight.status,
|
|
3683
|
-
agent_name: agent,
|
|
3684
|
-
project,
|
|
3685
|
-
portal,
|
|
3686
|
-
boot_sequence: MNEMO_AGENT_OS_KERNEL.boot_order,
|
|
3687
|
-
kernel_laws: MNEMO_AGENT_OS_KERNEL.laws,
|
|
3688
|
-
contract: MNEMO_AGENT_OS_KERNEL.contract,
|
|
3689
|
-
role_assignment: roleAssignment,
|
|
3690
|
-
role_contract: roleContract,
|
|
3691
|
-
preflight,
|
|
3692
|
-
recent_receipts: receipts,
|
|
3693
|
-
mission_control: {
|
|
3694
|
-
capacity: board.capacity,
|
|
3695
|
-
missing_roles: board.missing_roles,
|
|
3696
|
-
active_claims: board.active_claims,
|
|
3697
|
-
open_work_orders: board.open_work_orders,
|
|
3698
|
-
recommendations: board.recommendations
|
|
3699
|
-
},
|
|
3700
|
-
next_required_action: preflight.status === "ok" ? "Write workflow receipts while working and a handoff before stopping." : "Resolve preflight blockers before editing or deploying."
|
|
3701
|
-
};
|
|
3702
|
-
}
|
|
3703
|
-
|
|
3704
|
-
function agentCompanyBoard(db, input = {}) {
|
|
3705
|
-
ensureAgentGovernanceSchema(db);
|
|
3706
|
-
const scope = scopeName(input.scope);
|
|
3707
|
-
const project = textOrNull(input.project, 500);
|
|
3708
|
-
const roleRows = agentRoleList(db, { scope, project, include_inactive: false, limit: 1000 }).assignments || [];
|
|
3709
|
-
const contexts = portalContextList(db, { scope, project, limit: 1000 }).contexts || [];
|
|
3710
|
-
const role_counts = {};
|
|
3711
|
-
for (const row of roleRows) role_counts[row.role_name] = (role_counts[row.role_name] || 0) + 1;
|
|
3712
|
-
const missing_roles = GSTACK_ROLE_CATALOG.map((role) => role.role).filter((role) => !role_counts[role]);
|
|
3713
|
-
let active_claims = [];
|
|
3714
|
-
if (tableExists(db, "work_claim")) {
|
|
3715
|
-
const where = ["status IN ('active','stale')"];
|
|
3716
|
-
const params = [];
|
|
3717
|
-
if (project) { where.push("project=?"); params.push(project); }
|
|
3718
|
-
active_claims = db.prepare("SELECT id, project, agent_name, claim_kind, scope_value, file_path, status, expires_at, heartbeat_at FROM work_claim WHERE " + where.join(" AND ") + " ORDER BY heartbeat_at DESC LIMIT 500").all(...params);
|
|
3719
|
-
}
|
|
3720
|
-
let open_work_orders = [];
|
|
3721
|
-
if (tableExists(db, "work_order")) {
|
|
3722
|
-
const where = ["status NOT IN ('done','completed','cancelled')"];
|
|
3723
|
-
const params = [];
|
|
3724
|
-
if (project) { where.push("project=?"); params.push(project); }
|
|
3725
|
-
open_work_orders = db.prepare("SELECT id, project, title, assigned_agent, owner_agent, department_name, status, risk_class, updated_at FROM work_order WHERE " + where.join(" AND ") + " ORDER BY updated_at DESC LIMIT 100").all(...params);
|
|
3726
|
-
}
|
|
3727
|
-
return {
|
|
3728
|
-
ok: true,
|
|
3729
|
-
scope,
|
|
3730
|
-
project,
|
|
3731
|
-
capacity: {
|
|
3732
|
-
active_agent_roles: roleRows.length,
|
|
3733
|
-
active_claims: active_claims.length,
|
|
3734
|
-
portal_contexts: contexts.length,
|
|
3735
|
-
open_work_orders: open_work_orders.length,
|
|
3736
|
-
scale_target_agents: clampInt(input.scale_target_agents, 100, 1, 10000),
|
|
3737
|
-
coverage_percent: Math.round(((GSTACK_ROLE_CATALOG.length - missing_roles.length) / GSTACK_ROLE_CATALOG.length) * 100)
|
|
3738
|
-
},
|
|
3739
|
-
role_counts,
|
|
3740
|
-
missing_roles,
|
|
3741
|
-
active_roles: roleRows,
|
|
3742
|
-
portal_contexts: contexts,
|
|
3743
|
-
active_claims,
|
|
3744
|
-
open_work_orders,
|
|
3745
|
-
recommendations: [
|
|
3746
|
-
"Keep one Memory/Audit Officer active per busy portal.",
|
|
3747
|
-
"Route every task through mem_agent_company_preflight before code/text/pricing/design/logic changes.",
|
|
3748
|
-
"Shard 100 agents by portal + role + risk class; never by chat thread alone.",
|
|
3749
|
-
"Use capability tokens and evidence gates for risky or production work."
|
|
3750
|
-
]
|
|
3751
|
-
};
|
|
3752
|
-
}
|
|
3753
|
-
|
|
3754
|
-
function userIntentCapture(db, input = {}) {
|
|
3755
|
-
ensureAgentGovernanceSchema(db);
|
|
3756
|
-
const scope = scopeName(input.scope);
|
|
3757
|
-
const exactWords = textOrNull(input.exact_words || input.text || input.message || input.content, 8000);
|
|
3758
|
-
const summary = textOrNull(input.summary || input.intent || exactWords, 600);
|
|
3759
|
-
if (!summary) return { error: "summary_or_text required" };
|
|
3760
|
-
const project = normalizeProject(input.project || input.name) || null;
|
|
3761
|
-
const priority = inferUserIntentPriority((exactWords || "") + " " + summary, input.priority);
|
|
3762
|
-
let linkedTaskId = input.linked_task_id || input.task_id || null;
|
|
3763
|
-
const info = db.prepare(`
|
|
3764
|
-
INSERT INTO user_intent
|
|
3765
|
-
(scope, project, user_name, source_channel, message_ref, intent_kind, summary, exact_words, priority, status, linked_task_id, linked_work_order_id, meta_json)
|
|
3766
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
3767
|
-
`).run(
|
|
3768
|
-
scope,
|
|
3769
|
-
project,
|
|
3770
|
-
textOrNull(input.user_name || input.user || input.actor, 160),
|
|
3771
|
-
textOrNull(input.source_channel || input.channel || "telegram", 160),
|
|
3772
|
-
textOrNull(input.message_ref || input.source_ref, 1000),
|
|
3773
|
-
normalizeIntentKind(input.intent_kind || input.kind || "request"),
|
|
3774
|
-
summary,
|
|
3775
|
-
exactWords,
|
|
3776
|
-
priority,
|
|
3777
|
-
input.status || "captured",
|
|
3778
|
-
linkedTaskId || null,
|
|
3779
|
-
input.linked_work_order_id || input.work_order_id || null,
|
|
3780
|
-
safeJson(input.meta || {}, {})
|
|
3781
|
-
);
|
|
3782
|
-
const intentId = info.lastInsertRowid;
|
|
3783
|
-
let task = null;
|
|
3784
|
-
if (input.create_task === true && project) {
|
|
3785
|
-
const created = projectTaskCreate(db, {
|
|
3786
|
-
scope,
|
|
3787
|
-
project,
|
|
3788
|
-
surface: input.surface,
|
|
3789
|
-
title: input.task_title || summary,
|
|
3790
|
-
summary,
|
|
3791
|
-
category: input.intent_kind || input.kind || "user_request",
|
|
3792
|
-
priority,
|
|
3793
|
-
status: input.task_status || "open",
|
|
3794
|
-
owner_agent: input.owner_agent,
|
|
3795
|
-
assigned_agent: input.assigned_agent || input.agent_name,
|
|
3796
|
-
source_kind: "user_intent",
|
|
3797
|
-
source_id: String(intentId),
|
|
3798
|
-
source_ref: input.message_ref || input.source_ref,
|
|
3799
|
-
acceptance: input.acceptance || input.acceptance_criteria,
|
|
3800
|
-
created_by: input.agent_name || "mnemo",
|
|
3801
|
-
meta: { user_intent_id: intentId },
|
|
3802
|
-
});
|
|
3803
|
-
task = created.task || null;
|
|
3804
|
-
linkedTaskId = task && task.id || null;
|
|
3805
|
-
if (linkedTaskId) db.prepare("UPDATE user_intent SET linked_task_id=?, updated_at=strftime('%Y-%m-%dT%H:%M:%fZ','now') WHERE id=?").run(linkedTaskId, intentId);
|
|
3806
|
-
}
|
|
3807
|
-
if (input.set_focus === true && project) {
|
|
3808
|
-
projectFocusSet(db, {
|
|
3809
|
-
scope,
|
|
3810
|
-
project,
|
|
3811
|
-
surface: input.surface,
|
|
3812
|
-
active_target: input.active_target || summary,
|
|
3813
|
-
focus_summary: summary,
|
|
3814
|
-
must_do: input.must_do || input.acceptance,
|
|
3815
|
-
must_not_do: input.must_not_do || input.out_of_scope,
|
|
3816
|
-
owner_agent: input.owner_agent,
|
|
3817
|
-
coordinator_agent: input.coordinator_agent,
|
|
3818
|
-
source_ref: input.message_ref || input.source_ref,
|
|
3819
|
-
updated_by: input.agent_name || "mnemo",
|
|
3820
|
-
meta: { user_intent_id: intentId },
|
|
3821
|
-
});
|
|
3822
|
-
}
|
|
3823
|
-
return { ok: true, intent: rowToUserIntent(db.prepare("SELECT * FROM user_intent WHERE id=?").get(intentId)), task };
|
|
3824
|
-
}
|
|
3825
|
-
|
|
3826
|
-
function projectChannelPolicySet(db, input = {}) {
|
|
3827
|
-
ensureAgentGovernanceSchema(db);
|
|
3828
|
-
const scope = scopeName(input.scope);
|
|
3829
|
-
const project = normalizeProject(input.project || input.name);
|
|
3830
|
-
if (!project) return { error: "project required" };
|
|
3831
|
-
const defaultRules = [
|
|
3832
|
-
"Telegram is for coordination, short status, explicit questions, and human-facing updates.",
|
|
3833
|
-
"Mnemo Briefs are for durable assignments, findings, acceptance criteria, and cross-agent handoffs.",
|
|
3834
|
-
"Work Orders are the execution contract for scoped work; done is only accepted with evidence.",
|
|
3835
|
-
"Agents should propose useful next actions when a risk or project gap is visible, not wait for human phrasing."
|
|
3836
|
-
];
|
|
3837
|
-
db.prepare(`
|
|
3838
|
-
INSERT INTO project_channel_policy
|
|
3839
|
-
(scope, project, telegram_role, brief_role, work_order_role, rules_json, status, meta_json, updated_by, updated_at)
|
|
3840
|
-
VALUES (?,?,?,?,?,?,?,?,?,strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
3841
|
-
ON CONFLICT(scope, project) DO UPDATE SET
|
|
3842
|
-
telegram_role=excluded.telegram_role,
|
|
3843
|
-
brief_role=excluded.brief_role,
|
|
3844
|
-
work_order_role=excluded.work_order_role,
|
|
3845
|
-
rules_json=excluded.rules_json,
|
|
3846
|
-
status=excluded.status,
|
|
3847
|
-
meta_json=excluded.meta_json,
|
|
3848
|
-
updated_by=excluded.updated_by,
|
|
3849
|
-
updated_at=excluded.updated_at
|
|
3850
|
-
`).run(
|
|
3851
|
-
scope,
|
|
3852
|
-
project,
|
|
3853
|
-
textOrNull(input.telegram_role, 2000) || "Fast team coordination and human-visible updates; not durable truth.",
|
|
3854
|
-
textOrNull(input.brief_role, 2000) || "Durable cross-agent findings, assignments, acceptance criteria, and handoffs.",
|
|
3855
|
-
textOrNull(input.work_order_role, 2000) || "Scoped execution contract with owner, target, resources, evidence, and done gate.",
|
|
3856
|
-
safeJson(listInput(input.rules).length ? listInput(input.rules) : defaultRules, []),
|
|
3857
|
-
input.status || "active",
|
|
3858
|
-
safeJson(input.meta || {}, {}),
|
|
3859
|
-
normalizeAgentName(input.updated_by || input.agent_name || "") || null
|
|
3860
|
-
);
|
|
3861
|
-
return projectChannelPolicyGet(db, { scope, project });
|
|
3862
|
-
}
|
|
3863
|
-
|
|
3864
|
-
function projectChannelPolicyGet(db, input = {}) {
|
|
3865
|
-
ensureAgentGovernanceSchema(db);
|
|
3866
|
-
const project = normalizeProject(input.project || input.name);
|
|
3867
|
-
if (!project) return { error: "project required" };
|
|
3868
|
-
const row = db.prepare("SELECT * FROM project_channel_policy WHERE scope=? AND project=?").get(scopeName(input.scope), project);
|
|
3869
|
-
return { ok: !!row, policy: rowToProjectChannelPolicy(row), hint: row ? undefined : "Set policy with mem_project_channel_policy_set so Telegram, briefs, and Work Orders have clear roles." };
|
|
3870
|
-
}
|
|
3871
|
-
|
|
3872
|
-
function latestRows(db, sql, params, mapper) {
|
|
3873
|
-
try { return db.prepare(sql).all(...params).map(mapper || ((x) => x)); } catch { return []; }
|
|
3874
|
-
}
|
|
3875
|
-
|
|
3876
|
-
function projectBoard(db, input = {}) {
|
|
3877
|
-
ensureAgentGovernanceSchema(db);
|
|
3878
|
-
const scope = scopeName(input.scope);
|
|
3879
|
-
const project = normalizeProject(input.project || input.name);
|
|
3880
|
-
if (!project) return { error: "project required" };
|
|
3881
|
-
const limit = clampInt(input.limit, 25, 5, 100);
|
|
3882
|
-
const focus = projectFocusGet(db, { scope, project }).focus || null;
|
|
3883
|
-
const tasks = projectTaskList(db, { scope, project, include_done: input.include_done === true, limit }).tasks || [];
|
|
3884
|
-
const policy = projectChannelPolicyGet(db, { scope, project }).policy || null;
|
|
3885
|
-
const activeWorkOrders = latestRows(db, "SELECT * FROM work_order WHERE scope=? AND project=? AND status NOT IN ('done','closed','cancelled') ORDER BY updated_at DESC LIMIT ?", [scope, project, limit], rowToWorkOrder);
|
|
3886
|
-
const pendingBriefs = tableExists(db, "agent_brief")
|
|
3887
|
-
? latestRows(
|
|
3888
|
-
db,
|
|
3889
|
-
`SELECT b.id, b.agent_name, b.source_agent, b.status, substr(b.content,1,240) AS preview, b.created_at
|
|
3890
|
-
FROM agent_brief b
|
|
3891
|
-
WHERE b.status IN ('pending','dispatched')
|
|
3892
|
-
AND (b.content LIKE ? OR (json_valid(b.meta_json) AND json_extract(b.meta_json,'$.project')=?))
|
|
3893
|
-
AND (?=1 OR NOT EXISTS (
|
|
3894
|
-
SELECT 1 FROM project_task_ingest ti
|
|
3895
|
-
WHERE ti.scope=? AND ti.source_kind='agent_brief' AND ti.source_id=CAST(b.id AS TEXT)
|
|
3896
|
-
))
|
|
3897
|
-
ORDER BY b.created_at DESC LIMIT ?`,
|
|
3898
|
-
["%" + project + "%", project, input.include_ingested_briefs === true ? 1 : 0, scope, limit]
|
|
3899
|
-
)
|
|
3900
|
-
: [];
|
|
3901
|
-
const intents = latestRows(db, "SELECT * FROM user_intent WHERE scope=? AND (project=? OR project IS NULL) AND status NOT IN ('done','closed','cancelled') ORDER BY created_at DESC LIMIT ?", [scope, project, limit], rowToUserIntent);
|
|
3902
|
-
const byStatus = {};
|
|
3903
|
-
tasks.forEach((task) => {
|
|
3904
|
-
const key = task.status || "open";
|
|
3905
|
-
if (!byStatus[key]) byStatus[key] = [];
|
|
3906
|
-
byStatus[key].push(task);
|
|
3907
|
-
});
|
|
3908
|
-
const nextActions = [];
|
|
3909
|
-
if (!focus) nextActions.push("Set active project focus with mem_project_focus_set.");
|
|
3910
|
-
if (!policy) nextActions.push("Set channel policy so Telegram, briefs, and Work Orders are not mixed.");
|
|
3911
|
-
const critical = tasks.filter((task) => task.priority === "critical" && !["done", "closed", "cancelled"].includes(task.status));
|
|
3912
|
-
if (critical.length) nextActions.push("Resolve critical task #" + critical[0].id + ": " + critical[0].title);
|
|
3913
|
-
if (!activeWorkOrders.length && tasks.length) nextActions.push("Create a Work Order for the top open task before risky execution.");
|
|
3914
|
-
return {
|
|
3915
|
-
ok: true,
|
|
3916
|
-
project,
|
|
3917
|
-
focus,
|
|
3918
|
-
policy,
|
|
3919
|
-
summary: {
|
|
3920
|
-
open_tasks: tasks.filter((task) => !["done", "closed", "cancelled"].includes(task.status)).length,
|
|
3921
|
-
critical_tasks: critical.length,
|
|
3922
|
-
active_work_orders: activeWorkOrders.length,
|
|
3923
|
-
pending_briefs: pendingBriefs.length,
|
|
3924
|
-
open_intents: intents.length,
|
|
3925
|
-
},
|
|
3926
|
-
tasks_by_status: byStatus,
|
|
3927
|
-
active_work_orders: activeWorkOrders,
|
|
3928
|
-
pending_briefs: pendingBriefs,
|
|
3929
|
-
user_intents: intents,
|
|
3930
|
-
next_actions: nextActions,
|
|
3931
|
-
channel_rule: "Telegram coordinates; Mnemo briefs assign durable work; Work Orders authorize execution; Company Ledger remains truth.",
|
|
3932
|
-
};
|
|
3933
|
-
}
|
|
3934
|
-
|
|
3935
|
-
const AGENT_GOVERNANCE_TOOL_DEFS = {
|
|
3936
|
-
mem_company_governance_rulebook: {
|
|
3937
|
-
description: "Return the active ExampleCorp-wide company_agent_execution_governance_v1 rulebook: points 0-15, required evidence, runtime contract, and agent protocol. Agents must read this before code/build/deploy work.",
|
|
3938
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, agent_name: { type: "string" }, agent: { type: "string" }, task: { type: "string" }, summary: { type: "string" }, objective: { type: "string" } } }
|
|
3939
|
-
},
|
|
3940
|
-
mem_work_order_template_list: {
|
|
3941
|
-
description: "List agent-neutral Work Order templates. These are runtime-agnostic contracts for runtime adapters.",
|
|
3942
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, template_id: { type: "string" }, id: { type: "string" }, include_inactive: { type: "boolean" } } }
|
|
3943
|
-
},
|
|
3944
|
-
mem_work_order_template_upsert: {
|
|
3945
|
-
description: "Create/update a custom agent-neutral Work Order template. Templates are contracts; runtime adapters still execute through Mnemo gates.",
|
|
3946
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, template_id: { type: "string" }, id: { type: "string" }, title: { type: "string" }, description: { type: "string" }, objective: { type: "string" }, summary: { type: "string" }, department_name: { type: "string" }, department: { type: "string" }, risk_class: { type: "string" }, action_type: { type: "string" }, allowed_tools: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, tools: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, files: { type: "array", items: { type: "string" } }, routes: { type: "array", items: { type: "string" } }, domains: { type: "array", items: { type: "string" } }, system_names: { type: "array", items: { type: "string" } }, resources: { type: "array", items: { type: "object" } }, done_criteria: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, required_evidence: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, evidence_required: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, quality_gates: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, runtime_contract: { type: "object" }, status: { type: "string" }, source: { type: "string" }, updated_by: { type: "string" }, agent_name: { type: "string" }, meta: { type: "object" } }, required: ["template_id"] }
|
|
3947
|
-
},
|
|
3948
|
-
mem_work_order_create_from_template: {
|
|
3949
|
-
description: "Create a Work Order from a built-in or custom template and optionally issue a capability token. Same contract for all agent runtimes.",
|
|
3950
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, template_id: { type: "string" }, id: { type: "string" }, kind: { type: "string" }, project: { type: "string" }, title: { type: "string" }, objective: { type: "string" }, summary: { type: "string" }, task: { type: "string" }, department_name: { type: "string" }, department: { type: "string" }, owner_agent: { type: "string" }, assigned_agent: { type: "string" }, agent_name: { type: "string" }, risk_class: { type: "string" }, action_type: { type: "string" }, allowed_tools: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, tools: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, files: { type: "array", items: { type: "string" } }, routes: { type: "array", items: { type: "string" } }, domains: { type: "array", items: { type: "string" } }, system_names: { type: "array", items: { type: "string" } }, resources: { type: "array", items: { type: "object" } }, done_criteria: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, required_evidence: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, evidence_required: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, quality_gates: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, issue_token: { type: "boolean" }, ttl_minutes: { type: "integer" }, expires_at: { type: "string" }, source_ref: { type: "string" }, created_by: { type: "string" }, runtime_contract: { type: "object" }, meta: { type: "object" } }, required: ["template_id"] }
|
|
3951
|
-
},
|
|
3952
|
-
mem_quality_gate_template_list: {
|
|
3953
|
-
description: "List built-in agent-neutral quality gates used before Work Orders can be marked done.",
|
|
3954
|
-
inputSchema: { type: "object", properties: { gate_id: { type: "string" }, id: { type: "string" } } }
|
|
3955
|
-
},
|
|
3956
|
-
mem_quality_gate_run: {
|
|
3957
|
-
description: "Run an agent-neutral quality gate against concrete evidence. Failing gates should block done and force needs_review/blocked.",
|
|
3958
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, gate_id: { type: "string" }, template_id: { type: "string" }, gate: { type: "string" }, project: { type: "string" }, work_order_id: { type: "integer" }, agent_name: { type: "string" }, agent: { type: "string" }, evidence: { type: "array", items: { type: "object" } }, required_evidence: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, include_work_order_evidence: { type: "boolean" }, persist: { type: "boolean" }, meta: { type: "object" } } }
|
|
3959
|
-
},
|
|
3960
|
-
mem_context_snapshot_create: {
|
|
3961
|
-
description: "Save an agent-neutral context snapshot: decisions, remaining work, affected files/routes, branch, and Work Order link.",
|
|
3962
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, agent_name: { type: "string" }, agent: { type: "string" }, runtime_name: { type: "string" }, runtime: { type: "string" }, work_order_id: { type: "integer" }, title: { type: "string" }, summary: { type: "string" }, context: { type: "string" }, notes: { type: "string" }, decisions: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, remaining_work: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, remaining: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, next_steps: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, files: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, routes: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, urls: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, branch: { type: "string" }, commit_sha: { type: "string" }, commit: { type: "string" }, dirty: { type: "boolean" }, source_ref: { type: "string" }, status: { type: "string" }, meta: { type: "object" } }, required: ["summary"] }
|
|
3963
|
-
},
|
|
3964
|
-
mem_context_restore_brief: {
|
|
3965
|
-
description: "Return the latest saved context snapshot as a concise resume brief. Context is not company truth; agents must still check gates.",
|
|
3966
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, id: { type: "integer" }, snapshot_id: { type: "integer" }, project: { type: "string" }, agent_name: { type: "string" }, agent: { type: "string" }, work_order_id: { type: "integer" }, status: { type: "string" } } }
|
|
3967
|
-
},
|
|
3968
|
-
mem_work_order_create: {
|
|
3969
|
-
description: "Create a structured work order: objective, owner, department, assigned agent, scope, done criteria, risk, evidence, and optional capability token.",
|
|
3970
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, title: { type: "string" }, objective: { type: "string" }, summary: { type: "string" }, task: { type: "string" }, department_name: { type: "string" }, department: { type: "string" }, owner_agent: { type: "string" }, assigned_agent: { type: "string" }, agent_name: { type: "string" }, risk_class: { type: "string" }, action_type: { type: "string" }, allowed_tools: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, files: { type: "array", items: { type: "string" } }, routes: { type: "array", items: { type: "string" } }, domains: { type: "array", items: { type: "string" } }, system_names: { type: "array", items: { type: "string" } }, resources: { type: "array", items: { type: "object" } }, done_criteria: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, required_evidence: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, evidence_required: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, approval_ids: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, deadline_at: { type: "string" }, issue_token: { type: "boolean" }, ttl_minutes: { type: "integer" }, expires_at: { type: "string" }, source_ref: { type: "string" }, created_by: { type: "string" }, meta: { type: "object" } }, required: ["objective"] }
|
|
3971
|
-
},
|
|
3972
|
-
mem_work_order_list: {
|
|
3973
|
-
description: "List structured work orders by agent, owner, project, department, or status.",
|
|
3974
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, assigned_agent: { type: "string" }, agent_name: { type: "string" }, owner_agent: { type: "string" }, department_name: { type: "string" }, department: { type: "string" }, status: { type: "string" }, include_done: { type: "boolean" }, limit: { type: "integer" } } }
|
|
3975
|
-
},
|
|
3976
|
-
mem_work_order_complete: {
|
|
3977
|
-
description: "Complete a work order with evidence, optional handoff id, and completion summary. Token permission is not truth; completion needs evidence.",
|
|
3978
|
-
inputSchema: { type: "object", properties: { id: { type: "integer" }, work_order_id: { type: "integer" }, status: { type: "string" }, completion_summary: { type: "string" }, summary: { type: "string" }, result: { type: "string" }, handoff_id: { type: "integer" }, evidence: { type: "array", items: { type: "object" } } }, required: ["work_order_id"] }
|
|
3979
|
-
},
|
|
3980
|
-
mem_capability_token_issue: {
|
|
3981
|
-
description: "Issue a time-limited capability token for exactly one agent/work order/scope. Token is permission only, not truth.",
|
|
3982
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, work_order_id: { type: "integer" }, agent_name: { type: "string" }, department_name: { type: "string" }, project: { type: "string" }, risk_class: { type: "string" }, action_type: { type: "string" }, allowed_tools: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, tools: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, files: { type: "array", items: { type: "string" } }, routes: { type: "array", items: { type: "string" } }, domains: { type: "array", items: { type: "string" } }, system_names: { type: "array", items: { type: "string" } }, resources: { type: "array", items: { type: "object" } }, allowed_resources: { type: "object" }, required_evidence: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, approval_ids: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, budgets: { type: "object" }, granted_by: { type: "string" }, reason: { type: "string" }, ttl_minutes: { type: "integer" }, expires_at: { type: "string" }, meta: { type: "object" } }, required: ["agent_name"] }
|
|
3983
|
-
},
|
|
3984
|
-
mem_capability_token_check: {
|
|
3985
|
-
description: "Deterministically check whether a work step is covered by a valid token. Returns granted, reason, matched_scope, missing_approval, required_evidence, expires_at, and audit_id.",
|
|
3986
|
-
inputSchema: { type: "object", properties: { token_id: { type: "string" }, capability_token_id: { type: "string" }, work_order_id: { type: "integer" }, agent_name: { type: "string" }, project: { type: "string" }, task: { type: "string" }, summary: { type: "string" }, action_type: { type: "string" }, tool_name: { type: "string" }, files: { type: "array", items: { type: "string" } }, routes: { type: "array", items: { type: "string" } }, domains: { type: "array", items: { type: "string" } }, system_names: { type: "array", items: { type: "string" } }, resources: { type: "array", items: { type: "object" } }, approval_ids: { type: "array", items: { type: "string" } }, require_approval: { type: "boolean" } } }
|
|
3987
|
-
},
|
|
3988
|
-
mem_capability_token_revoke: {
|
|
3989
|
-
description: "Revoke a capability token and audit the revocation.",
|
|
3990
|
-
inputSchema: { type: "object", properties: { token_id: { type: "string" }, capability_token_id: { type: "string" }, revoked_by: { type: "string" }, agent_name: { type: "string" }, reason: { type: "string" }, meta: { type: "object" } }, required: ["token_id", "revoked_by"] }
|
|
3991
|
-
},
|
|
3992
|
-
mem_department_charter_set: {
|
|
3993
|
-
description: "Create/update a department charter: mission, responsibilities, boundaries, standard permissions, escalation rules, and autonomy bounds.",
|
|
3994
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, department_name: { type: "string" }, department: { type: "string" }, mission: { type: "string" }, responsibilities: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, boundaries: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, standard_permissions: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, files: { type: "array", items: { type: "string" } }, routes: { type: "array", items: { type: "string" } }, domains: { type: "array", items: { type: "string" } }, system_names: { type: "array", items: { type: "string" } }, resources: { type: "array", items: { type: "object" } }, escalation_rules: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, standing_permissions: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, autonomy_floor: { type: "integer" }, autonomy_ceiling: { type: "integer" }, default_risk_class: { type: "string" }, lead_agent: { type: "string" }, review_agent: { type: "string" }, status: { type: "string" }, updated_by: { type: "string" }, meta: { type: "object" } }, required: ["department_name"] }
|
|
3995
|
-
},
|
|
3996
|
-
mem_department_charter_get: {
|
|
3997
|
-
description: "Get one department charter.",
|
|
3998
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, department_name: { type: "string" }, department: { type: "string" } }, required: ["department_name"] }
|
|
3999
|
-
},
|
|
4000
|
-
mem_department_charter_list: {
|
|
4001
|
-
description: "List department charters.",
|
|
4002
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, status: { type: "string" }, limit: { type: "integer" } } }
|
|
4003
|
-
},
|
|
4004
|
-
mem_intent_route: {
|
|
4005
|
-
description: "Route an intent such as access request, decision, review, handoff, or incident to the right owner/department/coordinator.",
|
|
4006
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, intent_kind: { type: "string" }, intent: { type: "string" }, agent_name: { type: "string" }, project: { type: "string" }, department_name: { type: "string" }, department: { type: "string" }, resource_kind: { type: "string" }, resource_key: { type: "string" }, file_path: { type: "string" }, route: { type: "string" }, domain: { type: "string" }, system_name: { type: "string" }, summary: { type: "string" }, request: { type: "string" }, reason: { type: "string" }, route_to_agent: { type: "string" }, route_to_department: { type: "string" }, write_brief: { type: "boolean" }, meta: { type: "object" } } }
|
|
4007
|
-
},
|
|
4008
|
-
mem_autonomy_score_report: {
|
|
4009
|
-
description: "Compute a fact-based autonomy/trust score and suggested autonomy level from actions, briefs, findings, and token audit history.",
|
|
4010
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, agent_name: { type: "string" }, window_days: { type: "integer" }, days: { type: "integer" }, persist: { type: "boolean" } }, required: ["agent_name"] }
|
|
4011
|
-
},
|
|
4012
|
-
mem_project_focus_set: {
|
|
4013
|
-
description: "Set the active project focus so agents do not drift between portals, surfaces, or old tasks.",
|
|
4014
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, name: { type: "string" }, surface: { type: "string" }, portal: { type: "string" }, active_target: { type: "string" }, target: { type: "string" }, focus_summary: { type: "string" }, summary: { type: "string" }, objective: { type: "string" }, status: { type: "string" }, owner_agent: { type: "string" }, coordinator_agent: { type: "string" }, current_work_order_id: { type: "integer" }, work_order_id: { type: "integer" }, must_do: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, acceptance: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, must_not_do: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, out_of_scope: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, exclusions: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, source_ref: { type: "string" }, updated_by: { type: "string" }, agent_name: { type: "string" }, meta: { type: "object" } }, required: ["project"] }
|
|
4015
|
-
},
|
|
4016
|
-
mem_project_focus_get: {
|
|
4017
|
-
description: "Get the current active project focus.",
|
|
4018
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, name: { type: "string" } }, required: ["project"] }
|
|
4019
|
-
},
|
|
4020
|
-
mem_project_focus_list: {
|
|
4021
|
-
description: "List active project focuses across the company.",
|
|
4022
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, status: { type: "string" }, limit: { type: "integer" } } }
|
|
4023
|
-
},
|
|
4024
|
-
mem_project_task_create: {
|
|
4025
|
-
description: "Create a durable project task from a finding, user request, brief, or agent idea. This is not execution permission; create a Work Order before risky work.",
|
|
4026
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, name: { type: "string" }, surface: { type: "string" }, portal: { type: "string" }, title: { type: "string" }, summary: { type: "string" }, objective: { type: "string" }, description: { type: "string" }, task: { type: "string" }, category: { type: "string" }, kind: { type: "string" }, priority: { type: "string" }, status: { type: "string" }, owner_agent: { type: "string" }, assigned_agent: { type: "string" }, agent_name: { type: "string" }, assignee: { type: "string" }, source_kind: { type: "string" }, source_id: { type: "string" }, brief_id: { type: "integer" }, finding_id: { type: "integer" }, source_ref: { type: "string" }, message_ref: { type: "string" }, url: { type: "string" }, acceptance: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, acceptance_criteria: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, done_criteria: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, blockers: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, evidence: { type: "array", items: { type: "object" } }, linked_work_order_id: { type: "integer" }, work_order_id: { type: "integer" }, created_by: { type: "string" }, updated_by: { type: "string" }, meta: { type: "object" } }, required: ["project"] }
|
|
4027
|
-
},
|
|
4028
|
-
mem_project_task_update: {
|
|
4029
|
-
description: "Update a project task status, owner, evidence, blockers, or acceptance criteria.",
|
|
4030
|
-
inputSchema: { type: "object", properties: { id: { type: "integer" }, task_id: { type: "integer" }, project: { type: "string" }, surface: { type: "string" }, title: { type: "string" }, summary: { type: "string" }, objective: { type: "string" }, description: { type: "string" }, category: { type: "string" }, kind: { type: "string" }, priority: { type: "string" }, status: { type: "string" }, owner_agent: { type: "string" }, owner: { type: "string" }, assigned_agent: { type: "string" }, agent_name: { type: "string" }, assignee: { type: "string" }, acceptance: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, acceptance_criteria: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, done_criteria: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, blockers: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, evidence: { type: "array", items: { type: "object" } }, linked_work_order_id: { type: "integer" }, work_order_id: { type: "integer" }, updated_by: { type: "string" }, meta: { type: "object" } } }
|
|
4031
|
-
},
|
|
4032
|
-
mem_project_task_list: {
|
|
4033
|
-
description: "List durable project tasks by project, owner, assigned agent, status, or priority.",
|
|
4034
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, name: { type: "string" }, assigned_agent: { type: "string" }, agent_name: { type: "string" }, owner_agent: { type: "string" }, owner: { type: "string" }, status: { type: "string" }, include_done: { type: "boolean" }, limit: { type: "integer" } } }
|
|
4035
|
-
},
|
|
4036
|
-
mem_brief_task_ingest: {
|
|
4037
|
-
description: "Convert pending Mnemo agent briefs into durable project tasks with deterministic dedupe. Use this before project boards so old briefs stop reappearing as loose work.",
|
|
4038
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, brief_id: { type: "integer" }, brief_ids: { type: "array", items: { type: "integer" } }, agent_name: { type: "string" }, source_agent: { type: "string" }, project: { type: "string" }, default_project: { type: "string" }, status: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, statuses: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, limit: { type: "integer" }, title: { type: "string" }, summary: { type: "string" }, category: { type: "string" }, kind: { type: "string" }, priority: { type: "string" }, assigned_agent: { type: "string" }, assignee: { type: "string" }, owner_agent: { type: "string" }, owner: { type: "string" }, created_by: { type: "string" }, acceptance: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, acceptance_criteria: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, blockers: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, mark_brief_status: { type: "string" }, include_done: { type: "boolean" }, include_all_statuses: { type: "boolean" }, dry_run: { type: "boolean" } } }
|
|
4039
|
-
},
|
|
4040
|
-
mem_gstack_catalog: {
|
|
4041
|
-
description: "Return the Mnemo-native Agent OS adoption of gstack: kernel, role catalog, 23 core skills, commands, power tools, workflow gates, and 100-agent operating model.",
|
|
4042
|
-
inputSchema: { type: "object", properties: { include_power_tools: { type: "boolean" } } }
|
|
4043
|
-
},
|
|
4044
|
-
mem_agent_os_boot: {
|
|
4045
|
-
description: "Session-start boot gate for Mnemo Agent OS. Loads/sets role, checks portal context, claims, plan, conflicts, recent receipts, kernel contract, and mission-control state.",
|
|
4046
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, agent_name: { type: "string" }, role: { type: "string" }, role_name: { type: "string" }, project: { type: "string" }, portal: { type: "string" }, task: { type: "string" }, plan: { type: "string" }, plan_summary: { type: "string" }, files: { type: "array", items: { type: "string" } }, dependencies: { type: "array", items: { type: "string" } }, require_portal_context: { type: "boolean" }, require_work_claim: { type: "boolean" }, require_plan: { type: "boolean" }, replace_active: { type: "boolean" }, selected_by: { type: "string" }, scale_target_agents: { type: "integer" }, receipt_limit: { type: "integer" }, meta: { type: "object" } }, required: ["agent_name", "task"] }
|
|
4047
|
-
},
|
|
4048
|
-
mem_agent_role_select: {
|
|
4049
|
-
description: "Select the required gstack-style Mnemo role for an agent before work starts.",
|
|
4050
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, agent_name: { type: "string" }, role: { type: "string" }, project: { type: "string" }, portal: { type: "string" }, task: { type: "string" }, plan: { type: "string" }, plan_summary: { type: "string" }, replace_active: { type: "boolean" }, selected_by: { type: "string" }, updated_by: { type: "string" }, status: { type: "string" }, meta: { type: "object" } }, required: ["agent_name", "role"] }
|
|
4051
|
-
},
|
|
4052
|
-
mem_agent_role_get: {
|
|
4053
|
-
description: "Get the active gstack-style Mnemo role for an agent/project/portal.",
|
|
4054
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, agent_name: { type: "string" }, project: { type: "string" }, portal: { type: "string" } }, required: ["agent_name"] }
|
|
4055
|
-
},
|
|
4056
|
-
mem_agent_role_list: {
|
|
4057
|
-
description: "List active gstack-style role assignments across agents, projects, portals, and roles.",
|
|
4058
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, agent_name: { type: "string" }, project: { type: "string" }, role: { type: "string" }, status: { type: "string" }, include_inactive: { type: "boolean" }, limit: { type: "integer" } } }
|
|
4059
|
-
},
|
|
4060
|
-
mem_portal_context_set: {
|
|
4061
|
-
description: "Store required multiportal context: brand, domain, user role, language, design, credits, pricing, rights, and all rule layers.",
|
|
4062
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, portal: { type: "string" }, portal_id: { type: "string" }, portal_name: { type: "string" }, environment: { type: "string" }, country_or_market: { anyOf: [{ type: "array" }, { type: "string" }] }, company_name: { type: "string" }, company: { type: "string" }, company_or_brand: { type: "string" }, brand_owner: { type: "string" }, brand_name: { type: "string" }, brand: { type: "string" }, domain: { type: "string" }, user_role: { type: "string" }, language: { type: "string" }, language_default: { type: "string" }, supported_languages: { anyOf: [{ type: "array" }, { type: "string" }] }, locale: { type: "string" }, design: { type: "object" }, design_rules: { type: "object" }, theme_or_design_source: {}, credit_system: {}, credits: { type: "object" }, pricing: {}, prices: { type: "object" }, pricing_source_of_truth: {}, rights: { type: "object" }, permissions: { type: "object" }, billing_owner: { type: "string" }, auth_owner: { type: "string" }, deployment_owner: { type: "string" }, legal_owner: { type: "string" }, forbidden_cross_portal_leaks: { anyOf: [{ type: "array" }, { type: "string" }] }, shared_modules: { anyOf: [{ type: "array" }, { type: "string" }] }, protected_surfaces: { anyOf: [{ type: "array" }, { type: "string" }] }, global_rules: { anyOf: [{ type: "array" }, { type: "string" }] }, portal_rules: { anyOf: [{ type: "array" }, { type: "string" }] }, rules: { anyOf: [{ type: "array" }, { type: "string" }] }, customer_partner_rules: { anyOf: [{ type: "array" }, { type: "string" }] }, dependencies: { anyOf: [{ type: "array" }, { type: "string" }] }, required_checks: { anyOf: [{ type: "array" }, { type: "string" }] }, status: { type: "string" }, updated_by: { type: "string" }, agent_name: { type: "string" }, meta: { type: "object" } }, required: ["project"] }
|
|
4063
|
-
},
|
|
4064
|
-
mem_portal_context_get: {
|
|
4065
|
-
description: "Get the active multiportal context for a project/portal.",
|
|
4066
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, portal: { type: "string" } }, required: ["project"] }
|
|
4067
|
-
},
|
|
4068
|
-
mem_portal_context_list: {
|
|
4069
|
-
description: "List multiportal contexts.",
|
|
4070
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, status: { type: "string" }, include_inactive: { type: "boolean" }, limit: { type: "integer" } } }
|
|
4071
|
-
},
|
|
4072
|
-
mem_agent_company_preflight: {
|
|
4073
|
-
description: "Company-scale preflight for 100-agent operation. Blocks work until role, portal context, rule layers, work claims, plan receipt, and deploy precheck evidence are present.",
|
|
4074
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, agent_name: { type: "string" }, role: { type: "string" }, role_name: { type: "string" }, project: { type: "string" }, portal: { type: "string" }, task: { type: "string" }, action_type: { type: "string" }, plan: { type: "string" }, plan_summary: { type: "string" }, files: { type: "array", items: { type: "string" } }, dependencies: { type: "array", items: { type: "string" } }, evidence: { type: "array", items: { type: "object" } }, predeploy_evidence: { type: "array", items: { type: "object" } }, deploy_precheck_evidence: { type: "array", items: { type: "object" } }, require_portal_context: { type: "boolean" }, require_work_claim: { type: "boolean" }, require_plan: { type: "boolean" } }, required: ["agent_name", "task"] }
|
|
4075
|
-
},
|
|
4076
|
-
mem_workflow_receipt_create: {
|
|
4077
|
-
description: "Store a gstack-style workflow receipt for think/plan/build/review/test/ship/reflect/memorize, risks, decisions, handoffs, or evidence.",
|
|
4078
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, portal: { type: "string" }, agent_name: { type: "string" }, role: { type: "string" }, role_name: { type: "string" }, phase: { type: "string" }, summary: { type: "string" }, evidence: { type: "array", items: { type: "object" } }, risks: { type: "array", items: { type: "object" } }, affected_portals: { type: "array", items: { type: "string" } }, status: { type: "string" }, meta: { type: "object" } }, required: ["agent_name", "phase", "summary"] }
|
|
4079
|
-
},
|
|
4080
|
-
mem_rule_violation_log: {
|
|
4081
|
-
description: "Persist an owner/project/protected-scope rule violation with responsible agent, action, evidence, and prevention rule.",
|
|
4082
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, portal: { type: "string" }, agent_name: { type: "string" }, rule_key: { type: "string" }, rule_text: { type: "string" }, rule: { type: "string" }, summary: { type: "string" }, action_kind: { type: "string" }, action: { type: "string" }, target: { type: "string" }, file_path: { type: "string" }, route: { type: "string" }, system_name: { type: "string" }, severity: { type: "string" }, status: { type: "string" }, evidence: { type: "array", items: { type: "object" } }, prevention: {}, prevention_rule: {}, meta: { type: "object" } }, required: ["agent_name", "rule_key"] }
|
|
4083
|
-
},
|
|
4084
|
-
mem_owner_rule_diff: {
|
|
4085
|
-
description: "Snapshot current project owner rules and diff against the previous or specified snapshot so changed/added/removed rules are explicit.",
|
|
4086
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, name: { type: "string" }, before_snapshot_id: { type: "integer" }, snapshot_id: { type: "integer" }, create_snapshot: { type: "boolean" }, agent_name: { type: "string" }, created_by: { type: "string" }, updated_by: { type: "string" }, meta: { type: "object" } }, required: ["project"] }
|
|
4087
|
-
},
|
|
4088
|
-
mem_task_fingerprint: {
|
|
4089
|
-
description: "Create/check a semantic task fingerprint to prevent duplicate parallel work across similar wording, files, symptoms, messages, and portals.",
|
|
4090
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, name: { type: "string" }, portal: { type: "string" }, agent_name: { type: "string" }, summary: { type: "string" }, task: { type: "string" }, title: { type: "string" }, objective: { type: "string" }, customer_request: { type: "string" }, symptoms: { type: "string" }, message_ref: { type: "string" }, source_ref: { type: "string" }, files: { type: "array", items: { type: "string" } }, routes: { type: "array", items: { type: "string" } }, urls: { type: "array", items: { type: "string" } }, modules: { type: "array", items: { type: "string" } }, tags: { type: "array", items: { type: "string" } }, source_kind: { type: "string" }, source_id: { type: "string" }, linked_task_id: { type: "integer" }, task_id: { type: "integer" }, status: { type: "string" }, persist: { type: "boolean" }, similarity_threshold: { type: "number" }, meta: { type: "object" } } }
|
|
4091
|
-
},
|
|
4092
|
-
mem_never_again_check: {
|
|
4093
|
-
description: "Before acting, check active high-severity rule violations, incidents, scars, and duplicate task fingerprints that must not repeat.",
|
|
4094
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, name: { type: "string" }, portal: { type: "string" }, agent_name: { type: "string" }, summary: { type: "string" }, task: { type: "string" }, title: { type: "string" }, action: { type: "string" }, files: { type: "array", items: { type: "string" } }, routes: { type: "array", items: { type: "string" } }, symptoms: { type: "string" }, message_ref: { type: "string" } } }
|
|
4095
|
-
},
|
|
4096
|
-
mem_agent_blame_report: {
|
|
4097
|
-
description: "Audit report for accountability: actions, failures, rule violations, receipts, handoffs, and a reliability score by agent/project/window.",
|
|
4098
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, agent_name: { type: "string" }, project: { type: "string" }, name: { type: "string" }, days: { type: "integer" } } }
|
|
4099
|
-
},
|
|
4100
|
-
mem_completion_guard_check: {
|
|
4101
|
-
description: "Final completion guard: blocks COMPLETE unless evidence, tests, owner-rule/never-again checks, no blockers, and handoff are present.",
|
|
4102
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, agent_name: { type: "string" }, task: { type: "string" }, title: { type: "string" }, summary: { type: "string" }, work_order_id: { type: "integer" }, evidence: { type: "array", items: { type: "object" } }, required_evidence: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, requirements: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, tests: { type: "array", items: { type: "string" } }, handoff_id: { type: "integer" }, session_handoff_id: { type: "integer" }, blockers: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, open_blockers: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, require_evidence: { type: "boolean" }, require_tests: { type: "boolean" }, require_handoff: { type: "boolean" }, skip_never_again: { type: "boolean" } } }
|
|
4103
|
-
},
|
|
4104
|
-
mem_agent_company_board: {
|
|
4105
|
-
description: "Render a 100-agent operating board: roles, missing coverage, portal contexts, active claims, and open Work Orders.",
|
|
4106
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, scale_target_agents: { type: "integer" } } }
|
|
4107
|
-
},
|
|
4108
|
-
mem_user_intent_capture: {
|
|
4109
|
-
description: "Capture what the human actually wants in durable form, optionally creating a project task and/or active focus. Use this when user wording is business intent, not agent-internal thinking.",
|
|
4110
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, name: { type: "string" }, user_name: { type: "string" }, user: { type: "string" }, actor: { type: "string" }, source_channel: { type: "string" }, channel: { type: "string" }, message_ref: { type: "string" }, source_ref: { type: "string" }, intent_kind: { type: "string" }, kind: { type: "string" }, summary: { type: "string" }, intent: { type: "string" }, exact_words: { type: "string" }, text: { type: "string" }, message: { type: "string" }, content: { type: "string" }, priority: { type: "string" }, status: { type: "string" }, linked_task_id: { type: "integer" }, task_id: { type: "integer" }, linked_work_order_id: { type: "integer" }, work_order_id: { type: "integer" }, create_task: { type: "boolean" }, set_focus: { type: "boolean" }, task_title: { type: "string" }, task_status: { type: "string" }, surface: { type: "string" }, active_target: { type: "string" }, owner_agent: { type: "string" }, coordinator_agent: { type: "string" }, assigned_agent: { type: "string" }, agent_name: { type: "string" }, acceptance: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, acceptance_criteria: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, must_do: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, must_not_do: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, out_of_scope: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, meta: { type: "object" } } }
|
|
4111
|
-
},
|
|
4112
|
-
mem_project_channel_policy_set: {
|
|
4113
|
-
description: "Define what belongs in Telegram, Mnemo briefs, and Work Orders for one project so agent chatter does not become company truth.",
|
|
4114
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, name: { type: "string" }, telegram_role: { type: "string" }, brief_role: { type: "string" }, work_order_role: { type: "string" }, rules: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "string" }] }, status: { type: "string" }, updated_by: { type: "string" }, agent_name: { type: "string" }, meta: { type: "object" } }, required: ["project"] }
|
|
4115
|
-
},
|
|
4116
|
-
mem_project_channel_policy_get: {
|
|
4117
|
-
description: "Get the Telegram/Brief/Work-Order channel policy for one project.",
|
|
4118
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, name: { type: "string" } }, required: ["project"] }
|
|
4119
|
-
},
|
|
4120
|
-
mem_project_board: {
|
|
4121
|
-
description: "Render a project operating board: active focus, channel policy, tasks, active Work Orders, pending briefs, user intents, and next actions.",
|
|
4122
|
-
inputSchema: { type: "object", properties: { scope: { type: "string" }, project: { type: "string" }, name: { type: "string" }, include_done: { type: "boolean" }, include_ingested_briefs: { type: "boolean" }, limit: { type: "integer" } }, required: ["project"] }
|
|
4123
|
-
},
|
|
4124
|
-
};
|
|
4125
|
-
|
|
4126
|
-
function handleAgentGovernanceTool(db, name, input = {}) {
|
|
4127
|
-
if (name === "mem_company_governance_rulebook") return { handled: true, result: companyGovernanceRulebook(input || {}) };
|
|
4128
|
-
if (name === "mem_work_order_template_list") return { handled: true, result: workOrderTemplateList(db, input || {}) };
|
|
4129
|
-
if (name === "mem_work_order_template_upsert") return { handled: true, result: workOrderTemplateUpsert(db, input || {}) };
|
|
4130
|
-
if (name === "mem_work_order_create_from_template") return { handled: true, result: workOrderCreateFromTemplate(db, input || {}) };
|
|
4131
|
-
if (name === "mem_quality_gate_template_list") return { handled: true, result: qualityGateTemplateList(db, input || {}) };
|
|
4132
|
-
if (name === "mem_quality_gate_run") return { handled: true, result: qualityGateRun(db, input || {}) };
|
|
4133
|
-
if (name === "mem_context_snapshot_create") return { handled: true, result: contextSnapshotCreate(db, input || {}) };
|
|
4134
|
-
if (name === "mem_context_restore_brief") return { handled: true, result: contextRestoreBrief(db, input || {}) };
|
|
4135
|
-
if (name === "mem_work_order_create") return { handled: true, result: workOrderCreate(db, input || {}) };
|
|
4136
|
-
if (name === "mem_work_order_list") return { handled: true, result: workOrderList(db, input || {}) };
|
|
4137
|
-
if (name === "mem_work_order_complete") return { handled: true, result: workOrderComplete(db, input || {}) };
|
|
4138
|
-
if (name === "mem_capability_token_issue") return { handled: true, result: capabilityTokenIssue(db, input || {}) };
|
|
4139
|
-
if (name === "mem_capability_token_check") return { handled: true, result: capabilityTokenCheck(db, input || {}) };
|
|
4140
|
-
if (name === "mem_capability_token_revoke") return { handled: true, result: capabilityTokenRevoke(db, input || {}) };
|
|
4141
|
-
if (name === "mem_department_charter_set") return { handled: true, result: departmentCharterSet(db, input || {}) };
|
|
4142
|
-
if (name === "mem_department_charter_get") return { handled: true, result: departmentCharterGet(db, input || {}) };
|
|
4143
|
-
if (name === "mem_department_charter_list") return { handled: true, result: departmentCharterList(db, input || {}) };
|
|
4144
|
-
if (name === "mem_intent_route") return { handled: true, result: intentRoute(db, input || {}) };
|
|
4145
|
-
if (name === "mem_autonomy_score_report") return { handled: true, result: autonomyScoreReport(db, input || {}) };
|
|
4146
|
-
if (name === "mem_project_focus_set") return { handled: true, result: projectFocusSet(db, input || {}) };
|
|
4147
|
-
if (name === "mem_project_focus_get") return { handled: true, result: projectFocusGet(db, input || {}) };
|
|
4148
|
-
if (name === "mem_project_focus_list") return { handled: true, result: projectFocusList(db, input || {}) };
|
|
4149
|
-
if (name === "mem_project_task_create") return { handled: true, result: projectTaskCreate(db, input || {}) };
|
|
4150
|
-
if (name === "mem_project_task_update") return { handled: true, result: projectTaskUpdate(db, input || {}) };
|
|
4151
|
-
if (name === "mem_project_task_list") return { handled: true, result: projectTaskList(db, input || {}) };
|
|
4152
|
-
if (name === "mem_brief_task_ingest") return { handled: true, result: briefTaskIngest(db, input || {}) };
|
|
4153
|
-
if (name === "mem_gstack_catalog") return { handled: true, result: gstackCatalog(input || {}) };
|
|
4154
|
-
if (name === "mem_agent_os_boot") return { handled: true, result: agentOsBoot(db, input || {}) };
|
|
4155
|
-
if (name === "mem_agent_role_select") return { handled: true, result: agentRoleSelect(db, input || {}) };
|
|
4156
|
-
if (name === "mem_agent_role_get") return { handled: true, result: agentRoleGet(db, input || {}) };
|
|
4157
|
-
if (name === "mem_agent_role_list") return { handled: true, result: agentRoleList(db, input || {}) };
|
|
4158
|
-
if (name === "mem_portal_context_set") return { handled: true, result: portalContextSet(db, input || {}) };
|
|
4159
|
-
if (name === "mem_portal_context_get") return { handled: true, result: portalContextGet(db, input || {}) };
|
|
4160
|
-
if (name === "mem_portal_context_list") return { handled: true, result: portalContextList(db, input || {}) };
|
|
4161
|
-
if (name === "mem_agent_company_preflight") return { handled: true, result: agentCompanyPreflight(db, input || {}) };
|
|
4162
|
-
if (name === "mem_workflow_receipt_create") return { handled: true, result: workflowReceiptCreate(db, input || {}) };
|
|
4163
|
-
if (name === "mem_rule_violation_log") return { handled: true, result: ruleViolationLog(db, input || {}) };
|
|
4164
|
-
if (name === "mem_owner_rule_diff") return { handled: true, result: ownerRuleDiff(db, input || {}) };
|
|
4165
|
-
if (name === "mem_task_fingerprint") return { handled: true, result: taskFingerprint(db, input || {}) };
|
|
4166
|
-
if (name === "mem_never_again_check") return { handled: true, result: neverAgainCheck(db, input || {}) };
|
|
4167
|
-
if (name === "mem_agent_blame_report") return { handled: true, result: agentBlameReport(db, input || {}) };
|
|
4168
|
-
if (name === "mem_completion_guard_check") return { handled: true, result: completionGuardCheck(db, input || {}) };
|
|
4169
|
-
if (name === "mem_agent_company_board") return { handled: true, result: agentCompanyBoard(db, input || {}) };
|
|
4170
|
-
if (name === "mem_user_intent_capture") return { handled: true, result: userIntentCapture(db, input || {}) };
|
|
4171
|
-
if (name === "mem_project_channel_policy_set") return { handled: true, result: projectChannelPolicySet(db, input || {}) };
|
|
4172
|
-
if (name === "mem_project_channel_policy_get") return { handled: true, result: projectChannelPolicyGet(db, input || {}) };
|
|
4173
|
-
if (name === "mem_project_board") return { handled: true, result: projectBoard(db, input || {}) };
|
|
4174
|
-
return { handled: false };
|
|
4175
|
-
}
|
|
4176
|
-
|
|
4177
|
-
module.exports = {
|
|
4178
|
-
AGENT_GOVERNANCE_TOOL_DEFS,
|
|
4179
|
-
COMPANY_AGENT_EXECUTION_GOVERNANCE_GATE_ID,
|
|
4180
|
-
COMPANY_AGENT_EXECUTION_GOVERNANCE_VERSION,
|
|
4181
|
-
COMPANY_AGENT_EXECUTION_GOVERNANCE_POINTS,
|
|
4182
|
-
COMPANY_AGENT_EXECUTION_GOVERNANCE_REQUIRED_EVIDENCE,
|
|
4183
|
-
DEPLOY_PRECHECK_GATE_ID,
|
|
4184
|
-
DEPLOY_PRECHECK_REQUIRED_EVIDENCE,
|
|
4185
|
-
DEPLOY_REQUIRED_EVIDENCE,
|
|
4186
|
-
PREMORTEM_GATE_ID,
|
|
4187
|
-
PREMORTEM_REQUIRED_EVIDENCE,
|
|
4188
|
-
ANGEL_MOCKUP_GATE_ID,
|
|
4189
|
-
ANGEL_MOCKUP_REQUIRED_EVIDENCE,
|
|
4190
|
-
requiresAngelMockupGate,
|
|
4191
|
-
companyGovernanceRulebook,
|
|
4192
|
-
ensureAgentGovernanceSchema,
|
|
4193
|
-
handleAgentGovernanceTool,
|
|
4194
|
-
workOrderTemplateList,
|
|
4195
|
-
workOrderTemplateUpsert,
|
|
4196
|
-
workOrderCreateFromTemplate,
|
|
4197
|
-
qualityGateTemplateList,
|
|
4198
|
-
qualityGateRun,
|
|
4199
|
-
contextSnapshotCreate,
|
|
4200
|
-
contextRestoreBrief,
|
|
4201
|
-
workOrderCreate,
|
|
4202
|
-
workOrderList,
|
|
4203
|
-
workOrderComplete,
|
|
4204
|
-
capabilityTokenIssue,
|
|
4205
|
-
capabilityTokenCheck,
|
|
4206
|
-
capabilityTokenRevoke,
|
|
4207
|
-
requiresCapabilityToken,
|
|
4208
|
-
isDeployAction,
|
|
4209
|
-
departmentCharterSet,
|
|
4210
|
-
departmentCharterGet,
|
|
4211
|
-
departmentCharterList,
|
|
4212
|
-
intentRoute,
|
|
4213
|
-
autonomyScoreReport,
|
|
4214
|
-
projectFocusSet,
|
|
4215
|
-
projectFocusGet,
|
|
4216
|
-
projectFocusList,
|
|
4217
|
-
projectTaskCreate,
|
|
4218
|
-
projectTaskUpdate,
|
|
4219
|
-
projectTaskList,
|
|
4220
|
-
briefTaskIngest,
|
|
4221
|
-
gstackCatalog,
|
|
4222
|
-
agentOsBoot,
|
|
4223
|
-
agentRoleSelect,
|
|
4224
|
-
agentRoleGet,
|
|
4225
|
-
agentRoleList,
|
|
4226
|
-
portalContextSet,
|
|
4227
|
-
portalContextGet,
|
|
4228
|
-
portalContextList,
|
|
4229
|
-
agentCompanyPreflight,
|
|
4230
|
-
workflowReceiptCreate,
|
|
4231
|
-
ruleViolationLog,
|
|
4232
|
-
ownerRuleDiff,
|
|
4233
|
-
taskFingerprint,
|
|
4234
|
-
neverAgainCheck,
|
|
4235
|
-
agentBlameReport,
|
|
4236
|
-
completionGuardCheck,
|
|
4237
|
-
agentCompanyBoard,
|
|
4238
|
-
userIntentCapture,
|
|
4239
|
-
projectChannelPolicySet,
|
|
4240
|
-
projectChannelPolicyGet,
|
|
4241
|
-
projectBoard,
|
|
4242
|
-
};
|