thumbgate 1.34.3 → 1.37.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/.agents/skills/cyberstrike-compare-not-clone/SKILL.md +36 -0
- package/.agents/skills/gitlab-sandbox-allowlist-not-trust/SKILL.md +77 -0
- package/.agents/skills/jit-harness-compare-not-clone/SKILL.md +34 -0
- package/.agents/skills/openui-catalog-compose-honesty/SKILL.md +64 -0
- package/.agents/skills/zvec-grep-compare-not-clone/SKILL.md +34 -0
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/llms.txt +1 -0
- package/.well-known/mcp/server-card.json +1 -1
- package/CONTRIBUTING.md +95 -0
- package/README.md +195 -632
- package/THIRD_PARTY_NOTICES.md +89 -0
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/forge/forge.yaml +3 -3
- package/adapters/future-agi/.mcp.json +8 -0
- package/adapters/future-agi/FUTURE_AGI.md +23 -0
- package/adapters/future-agi/config.toml +3 -0
- package/adapters/future-agi/future-agi-bridge.js +9 -0
- package/adapters/future-agi/opencode.json +8 -0
- package/adapters/herdr/herdr-plugin.toml +18 -0
- package/adapters/mcp/server-stdio.js +238 -25
- package/adapters/opencode/opencode.json +1 -1
- package/adapters/workos/WORKOS.md +52 -0
- package/bin/cli.js +373 -5
- package/bin/futureagi-bridge +9 -0
- package/config/gate-templates.json +653 -4
- package/config/gates/actor-critic-audit.json +34 -0
- package/config/gates/default.json +21 -2
- package/config/gates/five-walls-governance.json +34 -0
- package/config/gates/future-agi-guardrails.json +34 -0
- package/config/gates/radware-threat-defense-2026.json +61 -0
- package/config/gates/simatree-data-governance.json +33 -0
- package/config/mcp-allowlists.json +4 -0
- package/config/merge-quality-checks.json +10 -1
- package/config/model-candidates.json +382 -24
- package/config/model-tiers.json +18 -0
- package/config/post-deploy-marketing-pages.json +10 -0
- package/config/progressive/01-wire-only.json +11 -0
- package/config/progressive/02-dashboard-empty-ok.json +10 -0
- package/config/progressive/03-one-lesson.json +10 -0
- package/config/progressive/04-warn-fires.json +11 -0
- package/config/progressive/05-strict-optional.json +11 -0
- package/config/progressive/README.md +15 -0
- package/config/schemas/broker-execution-receipt.schema.json +139 -0
- package/config/schemas/provider-execution-attestation-v1.schema.json +58 -0
- package/conformance/provider-attestation/vectors.json +320 -0
- package/docs/specs/provider-execution-attestation-v1.md +69 -0
- package/openapi/openapi.yaml +15 -0
- package/package.json +401 -147
- package/public/about.html +2 -2
- package/public/ai-malpractice-prevention.html +7 -7
- package/public/blog/a-10-dollar-vps-is-not-a-computer.html +143 -0
- package/public/blog/a-receipt-is-not-world-state.html +388 -0
- package/public/blog/git-at-agent-scale.html +374 -0
- package/public/blog/no-llm-in-the-gate.html +133 -0
- package/public/blog.html +80 -0
- package/public/case-studies.html +16 -1
- package/public/compare.html +28 -0
- package/public/diagnostic.html +216 -7
- package/public/docs/connectors.html +39 -0
- package/public/federal.html +2 -2
- package/public/founders.html +639 -0
- package/public/index.html +87 -9
- package/public/install.html +8 -8
- package/public/learn.html +39 -0
- package/public/numbers.html +2 -2
- package/public/peter.html +310 -0
- package/public/platform-partners.html +119 -0
- package/public/pricing.html +24 -3
- package/public/privacy.html +117 -0
- package/public/pro.html +17 -0
- package/public/support.html +62 -0
- package/public/terms.html +130 -0
- package/public/third-party-notices.html +95 -0
- package/public/yt.html +351 -0
- package/scripts/action-receipts.js +133 -3
- package/scripts/adaptive-governance-arena.js +349 -0
- package/scripts/admin-override.js +205 -0
- package/scripts/agent-action-inventory.js +869 -0
- package/scripts/agent-audit-trace.js +42 -2
- package/scripts/agent-egress-policy.js +1117 -0
- package/scripts/agent-memory-lifecycle.js +141 -2
- package/scripts/agent-operations-planner.js +441 -1
- package/scripts/agent-readiness.js +68 -0
- package/scripts/agent-security-central.js +647 -0
- package/scripts/allowlist-bridge-honesty.js +417 -0
- package/scripts/async-job-runner.js +102 -11
- package/scripts/audit-trail.js +212 -0
- package/scripts/auto-promote-gates.js +178 -27
- package/scripts/billing.js +1 -1
- package/scripts/broker-execution-receipts.js +719 -0
- package/scripts/budget-aware-gates-proof.js +423 -0
- package/scripts/claude-feedback-sync.js +29 -3
- package/scripts/claw-harness-production.js +237 -0
- package/scripts/cli-progress.js +111 -0
- package/scripts/cli-schema.js +163 -1
- package/scripts/codex-runbook-flywheel.js +318 -0
- package/scripts/context-footprint.js +186 -0
- package/scripts/contextfs.js +143 -61
- package/scripts/dashboard-limits.js +27 -0
- package/scripts/dashboard.js +279 -9
- package/scripts/deepseek-v4-runtime-guardrails.js +72 -6
- package/scripts/docker-sandbox-planner.js +18 -0
- package/scripts/double-blind-eval-protocol.js +252 -0
- package/scripts/edotenv-rl-gateway.js +259 -0
- package/scripts/ensure-production-search-corpus.js +162 -0
- package/scripts/eval-holdout.js +311 -0
- package/scripts/feedback-aggregate.js +21 -2
- package/scripts/feedback-loop.js +87 -5
- package/scripts/feedback-quality.js +9 -0
- package/scripts/file-ledger-lock.js +4 -1
- package/scripts/financial-control-plane.js +41 -1
- package/scripts/find-dormant-requires.js +118 -0
- package/scripts/fs-utils.js +84 -8
- package/scripts/gate-stats.js +2 -2
- package/scripts/gates-engine.js +859 -58
- package/scripts/generate-case-study-outreach.js +24 -15
- package/scripts/git-at-scale.js +628 -0
- package/scripts/governance-conflict-audit.js +1650 -0
- package/scripts/governance-difficulty-curriculum.js +328 -0
- package/scripts/graphrag-retrieval.js +275 -0
- package/scripts/gurobi-optimizer.js +324 -0
- package/scripts/gurobi_optimizer.py +485 -0
- package/scripts/harness-selector.js +82 -1
- package/scripts/hidden-entry-points.js +284 -0
- package/scripts/human-escalation.js +199 -1
- package/scripts/hybrid-feedback-context.js +152 -19
- package/scripts/intent-governed-execution.js +602 -0
- package/scripts/intervention-policy.js +123 -20
- package/scripts/jit-harness-compose.js +628 -0
- package/scripts/jsonl-watcher.js +10 -0
- package/scripts/lesson-embedding-index.js +95 -12
- package/scripts/lesson-retrieval.js +105 -19
- package/scripts/local-model-profile.js +19 -2
- package/scripts/mailer/resend-mailer.js +1 -1
- package/scripts/matryoshka-embedding.js +235 -0
- package/scripts/mcp-oauth.js +42 -4
- package/scripts/mcp-session-handles.js +1016 -0
- package/scripts/mcp-wiring-doctor.js +314 -0
- package/scripts/memory-firewall.js +115 -2
- package/scripts/memory-scope-readiness.js +299 -0
- package/scripts/memory-vs-rag-route.js +161 -0
- package/scripts/model-tier-router.js +148 -21
- package/scripts/nvidia-specdecode-al-doctor.js +536 -0
- package/scripts/openui-catalog-compose-honesty.js +593 -0
- package/scripts/operational-integrity.js +19 -1
- package/scripts/override-audit.js +213 -0
- package/scripts/package-manager-honesty-doctor.js +458 -0
- package/scripts/pr-manager.js +63 -1
- package/scripts/prove-herdr-adapter.js +52 -0
- package/scripts/prove-memory-pyramid-and-symbolic-canvas.js +95 -0
- package/scripts/prove-workos.js +73 -0
- package/scripts/provider-attestation-conformance.js +192 -0
- package/scripts/provider-receipt-contract.js +136 -0
- package/scripts/qwen38-max-cost-optimizer.js +401 -0
- package/scripts/radware-threat-defense.js +280 -0
- package/scripts/rag-embedding-identity.js +221 -0
- package/scripts/rag-precision-guardrails.js +112 -2
- package/scripts/remote-feedback-capture.js +159 -0
- package/scripts/research-agent-harness.js +256 -0
- package/scripts/rsi-safety-hillclimb.js +200 -0
- package/scripts/rule-sprawl.js +188 -0
- package/scripts/schedule-manager.js +147 -0
- package/scripts/self-heal.js +8 -0
- package/scripts/session-lease.js +415 -0
- package/scripts/simatree-data-governance.js +347 -0
- package/scripts/slo-alert-engine.js +172 -7
- package/scripts/solver-parity.js +539 -0
- package/scripts/stealth-memory-injection-gate.js +333 -0
- package/scripts/switchyard-router.js +366 -0
- package/scripts/telemetry-analytics.js +84 -27
- package/scripts/temporal-decay-weighting.js +138 -0
- package/scripts/test-all.js +165 -0
- package/scripts/token-savings.js +42 -0
- package/scripts/tool-kpi-tracker.js +108 -5
- package/scripts/tool-registry.js +193 -5
- package/scripts/universal-claim-evaluator.js +14 -2
- package/scripts/vector-store.js +279 -9
- package/scripts/workflow-notebook.js +391 -0
- package/scripts/workflow-sentinel.js +111 -12
- package/scripts/workos-production-guard.js +260 -0
- package/scripts/workspace-search-route.js +515 -0
- package/server.json +2 -2
- package/src/agent-identity-boundary.js +76 -0
- package/src/agent-retrieval-cache.js +155 -0
- package/src/alert-noise-ledger.js +502 -0
- package/src/api/server.js +802 -185
- package/src/git-fast-cache.js +220 -0
- package/src/git-wal-sync.js +156 -0
- package/src/hash-anchored-edit.js +82 -0
- package/src/hermes-platform-protocol.js +475 -0
- package/src/hermes-sync-plane.js +241 -0
- package/src/index.js +30 -1
- package/src/iso42001-compliance-guard.js +97 -0
- package/src/latency-budget.js +244 -0
- package/src/mcp-writeguard.js +316 -0
- package/src/miminions-adapter.js +106 -0
- package/src/pipeline-compass.js +104 -0
- package/src/ppl-alert-pipeline.js +284 -0
- package/src/rendezvous-router.js +90 -0
- package/src/security-questionnaire.js +195 -0
|
@@ -3,6 +3,29 @@
|
|
|
3
3
|
|
|
4
4
|
const MEMORY_TYPES = new Set(['episodic', 'semantic', 'procedural', 'preference', 'working']);
|
|
5
5
|
const MEMORY_SCOPES = new Set(['task', 'session', 'user', 'project', 'org']);
|
|
6
|
+
const DEFAULT_MAX_PROMOTE_CHARS = 12_000;
|
|
7
|
+
const TRANSPORT_TURN_RE = /^(user|assistant|system|human|tool|model)\s*:/gim;
|
|
8
|
+
const TRANSPORT_META_RE = /\b(conversation[_-]?id|message[_-]?id|tool_call_id|chat_completion|role\s*:\s*["']?(user|assistant|system))\b/i;
|
|
9
|
+
|
|
10
|
+
function isTransportTranscript(text, options = {}) {
|
|
11
|
+
const content = normalizeText(text);
|
|
12
|
+
if (!content) return false;
|
|
13
|
+
const minTurns = Number.isFinite(options.minTurns) ? options.minTurns : 3;
|
|
14
|
+
const turnMarkers = (content.match(TRANSPORT_TURN_RE) || []).length;
|
|
15
|
+
if (turnMarkers >= minTurns) return true;
|
|
16
|
+
const lineCount = content.split(/\n/).length;
|
|
17
|
+
if (lineCount >= 40 && turnMarkers >= 2) return true;
|
|
18
|
+
if (TRANSPORT_META_RE.test(content) && content.length >= 1500) return true;
|
|
19
|
+
if (/\b(raw transcript|conversation dump|full chat log)\b/i.test(content)) return true;
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isOversizedMemoryBlob(text, options = {}) {
|
|
24
|
+
const content = normalizeText(text);
|
|
25
|
+
const maxChars = Number.isFinite(options.maxChars) ? options.maxChars : DEFAULT_MAX_PROMOTE_CHARS;
|
|
26
|
+
return content.length > maxChars;
|
|
27
|
+
}
|
|
28
|
+
|
|
6
29
|
const HIGH_RISK_TERMS = new Set([
|
|
7
30
|
'billing',
|
|
8
31
|
'checkout',
|
|
@@ -260,6 +283,9 @@ function buildMemoryLifecyclePolicy(input = {}) {
|
|
|
260
283
|
semanticWeight: 0.5,
|
|
261
284
|
outcomeWeight: 0.25,
|
|
262
285
|
requireSourceAnchors: true,
|
|
286
|
+
rejectTransportTranscripts: true,
|
|
287
|
+
rejectOversizedBlobs: true,
|
|
288
|
+
maxPromoteChars: DEFAULT_MAX_PROMOTE_CHARS,
|
|
263
289
|
},
|
|
264
290
|
privacy: {
|
|
265
291
|
piiScanRequired: true,
|
|
@@ -269,12 +295,16 @@ function buildMemoryLifecyclePolicy(input = {}) {
|
|
|
269
295
|
};
|
|
270
296
|
}
|
|
271
297
|
|
|
298
|
+
|
|
272
299
|
function evaluateMemoryPromotion(memory = {}, policy = buildMemoryLifecyclePolicy()) {
|
|
273
300
|
const type = normalizeMemoryType(memory.type);
|
|
274
|
-
const content = normalizeText(memory.content);
|
|
301
|
+
const content = normalizeText(memory.content || collectMemoryText(memory));
|
|
275
302
|
const source = normalizeText(memory.source);
|
|
276
303
|
const outcome = normalizeText(memory.outcome);
|
|
277
304
|
const issues = [];
|
|
305
|
+
const maxChars = Number.isFinite(memory.maxPromoteChars)
|
|
306
|
+
? memory.maxPromoteChars
|
|
307
|
+
: DEFAULT_MAX_PROMOTE_CHARS;
|
|
278
308
|
|
|
279
309
|
if (!content) issues.push('missing_content');
|
|
280
310
|
if (!source) issues.push('missing_source_anchor');
|
|
@@ -285,19 +315,127 @@ function evaluateMemoryPromotion(memory = {}, policy = buildMemoryLifecyclePolic
|
|
|
285
315
|
if (type === 'preference' && memory.explicitUserSignal !== true) {
|
|
286
316
|
issues.push('preference_without_explicit_signal');
|
|
287
317
|
}
|
|
318
|
+
if (type === 'working' && memory.allowWorkingPromotion !== true) {
|
|
319
|
+
issues.push('working_memory_not_promotable');
|
|
320
|
+
}
|
|
321
|
+
if (isTransportTranscript(content)) {
|
|
322
|
+
issues.push('transport_transcript');
|
|
323
|
+
}
|
|
324
|
+
if (isOversizedMemoryBlob(content, { maxChars })) {
|
|
325
|
+
issues.push('oversized_blob');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const hardRetrievalBlocks = new Set([
|
|
329
|
+
'secret_like_content',
|
|
330
|
+
'transport_transcript',
|
|
331
|
+
'oversized_blob',
|
|
332
|
+
]);
|
|
333
|
+
const retrievalBlocked = issues.some((issue) => hardRetrievalBlocks.has(issue));
|
|
288
334
|
|
|
289
335
|
return {
|
|
290
336
|
type,
|
|
291
337
|
decision: issues.length === 0 ? 'promote' : 'hold',
|
|
292
338
|
issues,
|
|
293
|
-
retrievalEligible: issues.length === 0 || !issues.includes('
|
|
339
|
+
retrievalEligible: !retrievalBlocked && (issues.length === 0 || !issues.includes('missing_content')),
|
|
294
340
|
policyVersion: policy.generatedAt,
|
|
295
341
|
};
|
|
296
342
|
}
|
|
297
343
|
|
|
344
|
+
const PYRAMID_LAYERS = {
|
|
345
|
+
L0_CONVERSATION: 'L0_CONVERSATION',
|
|
346
|
+
L1_ATOM: 'L1_ATOM',
|
|
347
|
+
L2_SCENARIO: 'L2_SCENARIO',
|
|
348
|
+
L3_PERSONA_SOP: 'L3_PERSONA_SOP',
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
const SOP_LAYER_REGEX = /\b(sop|policy|prevention rule|guardrail|rule|directive|mandate|never allow|always require)\b/;
|
|
352
|
+
const WORKFLOW_LAYER_REGEX = /\b(workflow|scenario|pipeline|multi-step|sequence|playbook|recipe|lifecycle)\b/;
|
|
353
|
+
const LESSON_LAYER_REGEX = /\b(fact|lesson|observation|result|fix|patch|metric|verified)\b/;
|
|
354
|
+
|
|
355
|
+
function isSopLayer(type, tags, text) {
|
|
356
|
+
if (type === 'preference') return true;
|
|
357
|
+
if (tags.has('sop') || tags.has('rule') || tags.has('policy') || tags.has('guardrail')) return true;
|
|
358
|
+
return SOP_LAYER_REGEX.test(text);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function isWorkflowLayer(type, tags, text) {
|
|
362
|
+
if (type === 'procedural') return true;
|
|
363
|
+
if (tags.has('workflow') || tags.has('scenario') || tags.has('pattern')) return true;
|
|
364
|
+
return WORKFLOW_LAYER_REGEX.test(text);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function isAtomLayer(type, tags, text, memory) {
|
|
368
|
+
if (type === 'semantic') return true;
|
|
369
|
+
if (tags.has('fact') || tags.has('lesson') || tags.has('atom')) return true;
|
|
370
|
+
if (Boolean(memory.whatWentWrong) || Boolean(memory.whatWorked)) return true;
|
|
371
|
+
return LESSON_LAYER_REGEX.test(text);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function classifyPyramidLayer(memory = {}) {
|
|
375
|
+
const explicit = normalizeText(memory.pyramidLayer || memory.layer).toUpperCase();
|
|
376
|
+
if (PYRAMID_LAYERS[explicit]) return PYRAMID_LAYERS[explicit];
|
|
377
|
+
|
|
378
|
+
const type = normalizeMemoryType(memory.type);
|
|
379
|
+
const text = collectMemoryText(memory).toLowerCase();
|
|
380
|
+
const tagList = Array.isArray(memory.tags) ? memory.tags.map((t) => normalizeText(t).toLowerCase()) : [];
|
|
381
|
+
const tags = new Set(tagList);
|
|
382
|
+
|
|
383
|
+
if (isSopLayer(type, tags, text)) return PYRAMID_LAYERS.L3_PERSONA_SOP;
|
|
384
|
+
if (isWorkflowLayer(type, tags, text)) return PYRAMID_LAYERS.L2_SCENARIO;
|
|
385
|
+
if (isAtomLayer(type, tags, text, memory)) return PYRAMID_LAYERS.L1_ATOM;
|
|
386
|
+
return PYRAMID_LAYERS.L0_CONVERSATION;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
function distillMemoryPyramid(memories = [], options = {}) {
|
|
391
|
+
const safeMemories = Array.isArray(memories) ? memories : [];
|
|
392
|
+
const layers = {
|
|
393
|
+
[PYRAMID_LAYERS.L3_PERSONA_SOP]: [],
|
|
394
|
+
[PYRAMID_LAYERS.L2_SCENARIO]: [],
|
|
395
|
+
[PYRAMID_LAYERS.L1_ATOM]: [],
|
|
396
|
+
[PYRAMID_LAYERS.L0_CONVERSATION]: [],
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
for (const item of safeMemories) {
|
|
400
|
+
const layer = classifyPyramidLayer(item);
|
|
401
|
+
layers[layer].push({
|
|
402
|
+
...item,
|
|
403
|
+
pyramidLayer: layer,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const totalCount = safeMemories.length;
|
|
408
|
+
const charsPerToken = Math.max(1, Number(options.charsPerToken) || 4);
|
|
409
|
+
|
|
410
|
+
const layerStats = {};
|
|
411
|
+
for (const [layerKey, items] of Object.entries(layers)) {
|
|
412
|
+
const rawContent = items.map((m) => collectMemoryText(m)).join('\n');
|
|
413
|
+
const estimatedTokens = Math.ceil(rawContent.length / charsPerToken);
|
|
414
|
+
layerStats[layerKey] = {
|
|
415
|
+
count: items.length,
|
|
416
|
+
estimatedTokens,
|
|
417
|
+
sharePercent: totalCount > 0 ? Number(((items.length / totalCount) * 100).toFixed(1)) : 0,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return {
|
|
422
|
+
kind: 'semantic-memory-pyramid',
|
|
423
|
+
totalMemories: totalCount,
|
|
424
|
+
layers: layerStats,
|
|
425
|
+
pyramidItems: layers,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
298
429
|
module.exports = {
|
|
430
|
+
isTransportTranscript,
|
|
431
|
+
isOversizedMemoryBlob,
|
|
432
|
+
DEFAULT_MAX_PROMOTE_CHARS,
|
|
433
|
+
|
|
434
|
+
PYRAMID_LAYERS,
|
|
299
435
|
buildMemoryLifecyclePolicy,
|
|
300
436
|
buildMemoryLifecycleView,
|
|
437
|
+
classifyPyramidLayer,
|
|
438
|
+
distillMemoryPyramid,
|
|
301
439
|
evaluateMemoryPromotion,
|
|
302
440
|
extractMemoryEntities,
|
|
303
441
|
inferMemoryScope,
|
|
@@ -305,3 +443,4 @@ module.exports = {
|
|
|
305
443
|
scoreHybridMemoryMatch,
|
|
306
444
|
scoreMemoryDecay,
|
|
307
445
|
};
|
|
446
|
+
|
|
@@ -1,12 +1,426 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
4
6
|
function toList(value) {
|
|
5
7
|
if (Array.isArray(value)) return value.map(String).map((entry) => entry.trim()).filter(Boolean);
|
|
6
8
|
if (typeof value === 'string') return value.split(',').map((entry) => entry.trim()).filter(Boolean);
|
|
7
9
|
return [];
|
|
8
10
|
}
|
|
9
11
|
|
|
12
|
+
const BUSINESS_FUNCTION_AGENT_SOURCE = 'https://events.aiunleashedglobalsummit.com/aias-registration-yt';
|
|
13
|
+
const BUSINESS_FUNCTIONS = Object.freeze([
|
|
14
|
+
Object.freeze({ id: 'lead_generation', label: 'Lead Generation' }),
|
|
15
|
+
Object.freeze({ id: 'content_creation', label: 'Content Creation' }),
|
|
16
|
+
Object.freeze({ id: 'product_validation', label: 'Product Development & Validation' }),
|
|
17
|
+
Object.freeze({ id: 'marketing', label: 'Personalized Marketing' }),
|
|
18
|
+
Object.freeze({ id: 'sales_conversion', label: 'Sales & Conversion' }),
|
|
19
|
+
Object.freeze({ id: 'operations', label: 'Operations & Automation' }),
|
|
20
|
+
]);
|
|
21
|
+
const BUSINESS_FUNCTION_HANDOFF_EDGES = Object.freeze([
|
|
22
|
+
Object.freeze({ from: 'content_creation', to: 'marketing' }),
|
|
23
|
+
Object.freeze({ from: 'product_validation', to: 'marketing' }),
|
|
24
|
+
Object.freeze({ from: 'marketing', to: 'lead_generation' }),
|
|
25
|
+
Object.freeze({ from: 'lead_generation', to: 'sales_conversion' }),
|
|
26
|
+
Object.freeze({ from: 'sales_conversion', to: 'operations' }),
|
|
27
|
+
]);
|
|
28
|
+
const BUSINESS_FUNCTION_IDS = new Set(BUSINESS_FUNCTIONS.map((entry) => entry.id));
|
|
29
|
+
const BUSINESS_FUNCTION_EDGE_IDS = new Set(
|
|
30
|
+
BUSINESS_FUNCTION_HANDOFF_EDGES.map((edge) => `${edge.from}:${edge.to}`),
|
|
31
|
+
);
|
|
32
|
+
const BUSINESS_FUNCTION_RISKS = new Set(['low', 'medium', 'high']);
|
|
33
|
+
const INTERNAL_BUSINESS_ACTION = /^internal:/i;
|
|
34
|
+
|
|
35
|
+
function cleanText(value) {
|
|
36
|
+
return value === undefined || value === null ? '' : String(value).trim();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function cleanUniqueList(value) {
|
|
40
|
+
return Array.from(new Set(toList(value)));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function finiteNumber(value) {
|
|
44
|
+
// Reject booleans/arrays/objects — Number(true)===1 and Number([])===0 would
|
|
45
|
+
// silently pass zero-cost / ranking gates.
|
|
46
|
+
if (typeof value === 'number') {
|
|
47
|
+
return Number.isFinite(value) ? value : null;
|
|
48
|
+
}
|
|
49
|
+
if (typeof value !== 'string') return null;
|
|
50
|
+
const trimmed = value.trim();
|
|
51
|
+
if (!trimmed) return null;
|
|
52
|
+
const parsed = Number(trimmed);
|
|
53
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function roundNumber(value, decimals = 2) {
|
|
57
|
+
const factor = 10 ** decimals;
|
|
58
|
+
return Math.round((value + Number.EPSILON) * factor) / factor;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function hasMeasuredBaseline(baseline) {
|
|
62
|
+
if (!baseline || typeof baseline !== 'object') return false;
|
|
63
|
+
const measuredAt = cleanText(baseline.measuredAt);
|
|
64
|
+
return finiteNumber(baseline.value) !== null
|
|
65
|
+
&& Boolean(cleanText(baseline.unit))
|
|
66
|
+
&& Boolean(cleanText(baseline.source))
|
|
67
|
+
&& Boolean(measuredAt)
|
|
68
|
+
&& Number.isFinite(Date.parse(measuredAt));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function failedBusinessRules(rules) {
|
|
72
|
+
return rules.filter(([valid]) => !valid).map(([, issue]) => issue);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function calculateBusinessCandidateEstimates(values) {
|
|
76
|
+
const {
|
|
77
|
+
estimatedMonthlyValueUsd,
|
|
78
|
+
recurringMonthlyCostUsd,
|
|
79
|
+
implementationCostUsd,
|
|
80
|
+
confidence,
|
|
81
|
+
} = values;
|
|
82
|
+
const valueInputsValid = estimatedMonthlyValueUsd !== null
|
|
83
|
+
&& estimatedMonthlyValueUsd > 0
|
|
84
|
+
&& recurringMonthlyCostUsd !== null
|
|
85
|
+
&& recurringMonthlyCostUsd >= 0
|
|
86
|
+
&& confidence !== null
|
|
87
|
+
&& confidence > 0
|
|
88
|
+
&& confidence <= 1;
|
|
89
|
+
const expectedMonthlyNetValueUsd = valueInputsValid
|
|
90
|
+
? roundNumber((estimatedMonthlyValueUsd * confidence) - recurringMonthlyCostUsd)
|
|
91
|
+
: null;
|
|
92
|
+
const costInputsValid = implementationCostUsd !== null
|
|
93
|
+
&& implementationCostUsd > 0
|
|
94
|
+
&& expectedMonthlyNetValueUsd !== null
|
|
95
|
+
&& expectedMonthlyNetValueUsd > 0;
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
expectedMonthlyNetValueUsd,
|
|
99
|
+
paybackMonths: costInputsValid
|
|
100
|
+
? roundNumber(implementationCostUsd / expectedMonthlyNetValueUsd, 3)
|
|
101
|
+
: null,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function normalizeBusinessFunctionCandidate(input = {}) {
|
|
106
|
+
const functionId = cleanText(input.functionId);
|
|
107
|
+
const estimatedMonthlyValueUsd = finiteNumber(input.estimatedMonthlyValueUsd);
|
|
108
|
+
const recurringMonthlyCostUsd = finiteNumber(input.recurringMonthlyCostUsd);
|
|
109
|
+
const implementationCostUsd = finiteNumber(input.implementationCostUsd);
|
|
110
|
+
const implementationHours = finiteNumber(input.implementationHours);
|
|
111
|
+
const confidence = finiteNumber(input.confidence);
|
|
112
|
+
const risk = cleanText(input.risk).toLowerCase();
|
|
113
|
+
const evidence = cleanUniqueList(input.evidence);
|
|
114
|
+
const estimates = calculateBusinessCandidateEstimates({
|
|
115
|
+
estimatedMonthlyValueUsd,
|
|
116
|
+
recurringMonthlyCostUsd,
|
|
117
|
+
implementationCostUsd,
|
|
118
|
+
confidence,
|
|
119
|
+
});
|
|
120
|
+
const issues = failedBusinessRules([
|
|
121
|
+
[BUSINESS_FUNCTION_IDS.has(functionId), 'unsupported_business_function'],
|
|
122
|
+
[Boolean(cleanText(input.objective)), 'missing_objective'],
|
|
123
|
+
[Boolean(cleanText(input.primaryKpi)), 'missing_primary_kpi'],
|
|
124
|
+
[Boolean(cleanText(input.inputSource)), 'missing_input_source'],
|
|
125
|
+
[hasMeasuredBaseline(input.baseline), 'missing_measured_baseline'],
|
|
126
|
+
[estimatedMonthlyValueUsd !== null && estimatedMonthlyValueUsd > 0, 'missing_estimated_monthly_value_usd'],
|
|
127
|
+
[recurringMonthlyCostUsd !== null && recurringMonthlyCostUsd >= 0, 'missing_recurring_monthly_cost_usd'],
|
|
128
|
+
[implementationCostUsd !== null && implementationCostUsd > 0, 'missing_implementation_cost_usd'],
|
|
129
|
+
[implementationHours !== null && implementationHours > 0, 'missing_implementation_hours'],
|
|
130
|
+
[confidence !== null && confidence > 0 && confidence <= 1, 'invalid_confidence'],
|
|
131
|
+
[BUSINESS_FUNCTION_RISKS.has(risk), 'invalid_risk'],
|
|
132
|
+
[evidence.length > 0, 'missing_evidence'],
|
|
133
|
+
[
|
|
134
|
+
estimates.expectedMonthlyNetValueUsd === null || estimates.expectedMonthlyNetValueUsd > 0,
|
|
135
|
+
'non_positive_expected_monthly_net_value',
|
|
136
|
+
],
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
agentId: cleanText(input.agentId) || functionId || 'unnamed-agent',
|
|
141
|
+
functionId: functionId || null,
|
|
142
|
+
objective: cleanText(input.objective) || null,
|
|
143
|
+
primaryKpi: cleanText(input.primaryKpi) || null,
|
|
144
|
+
inputSource: cleanText(input.inputSource) || null,
|
|
145
|
+
baseline: hasMeasuredBaseline(input.baseline) ? {
|
|
146
|
+
value: finiteNumber(input.baseline.value),
|
|
147
|
+
unit: cleanText(input.baseline.unit),
|
|
148
|
+
measuredAt: new Date(input.baseline.measuredAt).toISOString(),
|
|
149
|
+
source: cleanText(input.baseline.source),
|
|
150
|
+
} : null,
|
|
151
|
+
estimates: {
|
|
152
|
+
estimatedMonthlyValueUsd,
|
|
153
|
+
recurringMonthlyCostUsd,
|
|
154
|
+
implementationCostUsd,
|
|
155
|
+
implementationHours,
|
|
156
|
+
confidence,
|
|
157
|
+
...estimates,
|
|
158
|
+
},
|
|
159
|
+
risk: risk || null,
|
|
160
|
+
evidence,
|
|
161
|
+
status: issues.length === 0 ? 'evidence_complete' : 'insufficient_evidence',
|
|
162
|
+
launchGate: risk === 'high' ? 'human_review_required' : 'standard_governance',
|
|
163
|
+
issues: Array.from(new Set(issues)),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function rejectDuplicateBusinessAgentIds(candidates) {
|
|
168
|
+
const counts = new Map();
|
|
169
|
+
for (const candidate of candidates) {
|
|
170
|
+
counts.set(candidate.agentId, (counts.get(candidate.agentId) || 0) + 1);
|
|
171
|
+
}
|
|
172
|
+
return candidates.map((candidate) => (
|
|
173
|
+
counts.get(candidate.agentId) > 1
|
|
174
|
+
? {
|
|
175
|
+
...candidate,
|
|
176
|
+
status: 'insufficient_evidence',
|
|
177
|
+
issues: Array.from(new Set([...candidate.issues, 'duplicate_agent_id'])),
|
|
178
|
+
}
|
|
179
|
+
: candidate
|
|
180
|
+
));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function rankBusinessFunctionAgents(candidates = []) {
|
|
184
|
+
const normalized = Array.isArray(candidates)
|
|
185
|
+
? rejectDuplicateBusinessAgentIds(candidates.map(normalizeBusinessFunctionCandidate))
|
|
186
|
+
: [];
|
|
187
|
+
const eligible = normalized
|
|
188
|
+
.filter((candidate) => candidate.status === 'evidence_complete')
|
|
189
|
+
.sort((left, right) => (
|
|
190
|
+
left.estimates.paybackMonths - right.estimates.paybackMonths
|
|
191
|
+
|| right.estimates.expectedMonthlyNetValueUsd - left.estimates.expectedMonthlyNetValueUsd
|
|
192
|
+
|| left.estimates.implementationHours - right.estimates.implementationHours
|
|
193
|
+
|| left.agentId.localeCompare(right.agentId)
|
|
194
|
+
));
|
|
195
|
+
const ranks = new Map(eligible.map((candidate, index) => [candidate.agentId, index + 1]));
|
|
196
|
+
const rankedCandidates = normalized.map((candidate) => ({
|
|
197
|
+
...candidate,
|
|
198
|
+
rank: ranks.get(candidate.agentId) || null,
|
|
199
|
+
})).sort((left, right) => {
|
|
200
|
+
if (left.rank === null && right.rank === null) return left.agentId.localeCompare(right.agentId);
|
|
201
|
+
if (left.rank === null) return 1;
|
|
202
|
+
if (right.rank === null) return -1;
|
|
203
|
+
return left.rank - right.rank;
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
schemaVersion: 'business-function-agent-ranking-v1',
|
|
208
|
+
status: eligible.length > 0 ? 'ranked_estimates' : 'insufficient_evidence',
|
|
209
|
+
source: {
|
|
210
|
+
url: BUSINESS_FUNCTION_AGENT_SOURCE,
|
|
211
|
+
claimsVerifiedByThumbGate: false,
|
|
212
|
+
},
|
|
213
|
+
methodology: {
|
|
214
|
+
estimated: true,
|
|
215
|
+
formula: 'paybackMonths = implementationCostUsd / ((estimatedMonthlyValueUsd * confidence) - recurringMonthlyCostUsd)',
|
|
216
|
+
warning: 'Planning estimate; not achieved revenue or production proof.',
|
|
217
|
+
},
|
|
218
|
+
candidateCount: normalized.length,
|
|
219
|
+
evidenceCompleteCount: eligible.length,
|
|
220
|
+
recommendedPilot: eligible.length > 0 ? eligible[0].agentId : null,
|
|
221
|
+
candidates: rankedCandidates,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function validBusinessObjectSchema(schema) {
|
|
226
|
+
const properties = schema?.properties;
|
|
227
|
+
// Arrays are typeof 'object' — reject them so properties: ['x'] cannot pass.
|
|
228
|
+
if (
|
|
229
|
+
schema?.type !== 'object'
|
|
230
|
+
|| !properties
|
|
231
|
+
|| typeof properties !== 'object'
|
|
232
|
+
|| Array.isArray(properties)
|
|
233
|
+
) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
const propertyNames = Object.keys(properties);
|
|
237
|
+
return propertyNames.length > 0
|
|
238
|
+
&& Array.isArray(schema.required)
|
|
239
|
+
&& schema.required.length > 0
|
|
240
|
+
&& schema.required.every((field) => propertyNames.includes(field));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function validBusinessConsent(consent) {
|
|
244
|
+
if (consent?.verified !== true) return false;
|
|
245
|
+
const recordedAt = cleanText(consent.recordedAt);
|
|
246
|
+
return Boolean(cleanText(consent.source))
|
|
247
|
+
&& Boolean(recordedAt)
|
|
248
|
+
&& Number.isFinite(Date.parse(recordedAt));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function validBusinessApproval(approval) {
|
|
252
|
+
return approval?.status === 'approved' && Boolean(cleanText(approval.approvalId));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function buildBusinessHandoffIssues(context) {
|
|
256
|
+
const {
|
|
257
|
+
input,
|
|
258
|
+
fromFunction,
|
|
259
|
+
toFunction,
|
|
260
|
+
requestedActions,
|
|
261
|
+
requiredEvidence,
|
|
262
|
+
qualificationEvidence,
|
|
263
|
+
dataScopes,
|
|
264
|
+
estimatedCostUsd,
|
|
265
|
+
maxCostUsd,
|
|
266
|
+
slaMinutes,
|
|
267
|
+
} = context;
|
|
268
|
+
const isLeadToSales = fromFunction === 'lead_generation' && toFunction === 'sales_conversion';
|
|
269
|
+
const externalActionRequested = requestedActions.some((action) => !INTERNAL_BUSINESS_ACTION.test(action));
|
|
270
|
+
|
|
271
|
+
return failedBusinessRules([
|
|
272
|
+
[BUSINESS_FUNCTION_IDS.has(fromFunction), 'unsupported_source_function'],
|
|
273
|
+
[BUSINESS_FUNCTION_IDS.has(toFunction), 'unsupported_target_function'],
|
|
274
|
+
[BUSINESS_FUNCTION_EDGE_IDS.has(`${fromFunction}:${toFunction}`), 'undeclared_handoff_edge'],
|
|
275
|
+
[Boolean(cleanText(input.workflowId)), 'missing_workflow_id'],
|
|
276
|
+
[Boolean(cleanText(input.correlationId)), 'missing_correlation_id'],
|
|
277
|
+
[Boolean(cleanText(input.objective)), 'missing_objective'],
|
|
278
|
+
[Boolean(cleanText(input.primaryKpi)), 'missing_primary_kpi'],
|
|
279
|
+
[Boolean(cleanText(input.expectedOutcome)), 'missing_expected_outcome'],
|
|
280
|
+
[validBusinessObjectSchema(input.inputSchema), 'invalid_input_schema'],
|
|
281
|
+
[validBusinessObjectSchema(input.outputSchema), 'invalid_output_schema'],
|
|
282
|
+
[dataScopes.length > 0, 'missing_data_scope'],
|
|
283
|
+
[typeof input.containsPersonalData === 'boolean', 'missing_data_classification'],
|
|
284
|
+
[requiredEvidence.length > 0, 'missing_required_evidence'],
|
|
285
|
+
[Boolean(cleanText(input.idempotencyKey)), 'missing_idempotency_key'],
|
|
286
|
+
[estimatedCostUsd !== null && estimatedCostUsd >= 0, 'invalid_estimated_cost_usd'],
|
|
287
|
+
[maxCostUsd !== null && maxCostUsd >= 0, 'invalid_max_cost_usd'],
|
|
288
|
+
[estimatedCostUsd === null || maxCostUsd === null || estimatedCostUsd <= maxCostUsd, 'handoff_over_budget'],
|
|
289
|
+
[slaMinutes !== null && slaMinutes > 0, 'invalid_sla_minutes'],
|
|
290
|
+
[input.containsPersonalData !== true || validBusinessConsent(input.consent), 'personal_data_requires_verified_consent'],
|
|
291
|
+
[!isLeadToSales || qualificationEvidence.length > 0, 'lead_to_sales_requires_qualification_evidence'],
|
|
292
|
+
[!isLeadToSales || validBusinessConsent(input.consent), 'lead_to_sales_requires_verified_consent'],
|
|
293
|
+
[!externalActionRequested || validBusinessApproval(input.approval), 'external_action_requires_approval_receipt'],
|
|
294
|
+
]);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function buildBusinessOutcomeReceiptTemplate(contract) {
|
|
298
|
+
return {
|
|
299
|
+
schemaVersion: 'task-outcome-template-v1',
|
|
300
|
+
recordable: false,
|
|
301
|
+
verificationState: 'not_run',
|
|
302
|
+
reason: 'Add verified results before recording.',
|
|
303
|
+
taskOutcome: {
|
|
304
|
+
taskId: contract.correlationId,
|
|
305
|
+
taskType: `business_handoff:${contract.fromFunction}:${contract.toFunction}`,
|
|
306
|
+
goal: contract.objective,
|
|
307
|
+
expectedOutcome: contract.expectedOutcome,
|
|
308
|
+
status: null,
|
|
309
|
+
verification: { performed: false, passed: false, evidence: [] },
|
|
310
|
+
toolCalls: [],
|
|
311
|
+
policy: null,
|
|
312
|
+
efficiency: null,
|
|
313
|
+
businessOutcome: null,
|
|
314
|
+
metadata: {
|
|
315
|
+
workflowId: contract.workflowId,
|
|
316
|
+
primaryKpi: contract.primaryKpi,
|
|
317
|
+
estimateOnly: true,
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function evaluateBusinessFunctionHandoff(input = {}) {
|
|
324
|
+
const fromFunction = cleanText(input.fromFunction);
|
|
325
|
+
const toFunction = cleanText(input.toFunction);
|
|
326
|
+
const requestedActions = cleanUniqueList(input.requestedActions);
|
|
327
|
+
const requiredEvidence = cleanUniqueList(input.requiredEvidence);
|
|
328
|
+
const qualificationEvidence = cleanUniqueList(input.qualificationEvidence);
|
|
329
|
+
const dataScopes = cleanUniqueList(input.dataScopes);
|
|
330
|
+
const estimatedCostUsd = finiteNumber(input.budget?.estimatedCostUsd);
|
|
331
|
+
const maxCostUsd = finiteNumber(input.budget?.maxCostUsd);
|
|
332
|
+
const slaMinutes = finiteNumber(input.slaMinutes);
|
|
333
|
+
const issues = buildBusinessHandoffIssues({
|
|
334
|
+
input,
|
|
335
|
+
fromFunction,
|
|
336
|
+
toFunction,
|
|
337
|
+
requestedActions,
|
|
338
|
+
requiredEvidence,
|
|
339
|
+
qualificationEvidence,
|
|
340
|
+
dataScopes,
|
|
341
|
+
estimatedCostUsd,
|
|
342
|
+
maxCostUsd,
|
|
343
|
+
slaMinutes,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
const contract = {
|
|
347
|
+
contractVersion: 'business-function-handoff-v1',
|
|
348
|
+
workflowId: cleanText(input.workflowId) || null,
|
|
349
|
+
correlationId: cleanText(input.correlationId) || null,
|
|
350
|
+
fromFunction: fromFunction || null,
|
|
351
|
+
toFunction: toFunction || null,
|
|
352
|
+
objective: cleanText(input.objective) || null,
|
|
353
|
+
primaryKpi: cleanText(input.primaryKpi) || null,
|
|
354
|
+
expectedOutcome: cleanText(input.expectedOutcome) || null,
|
|
355
|
+
inputSchema: validBusinessObjectSchema(input.inputSchema) ? input.inputSchema : null,
|
|
356
|
+
outputSchema: validBusinessObjectSchema(input.outputSchema) ? input.outputSchema : null,
|
|
357
|
+
dataScopes,
|
|
358
|
+
containsPersonalData: input.containsPersonalData === true,
|
|
359
|
+
consent: validBusinessConsent(input.consent) ? {
|
|
360
|
+
verified: true,
|
|
361
|
+
source: cleanText(input.consent.source),
|
|
362
|
+
recordedAt: new Date(input.consent.recordedAt).toISOString(),
|
|
363
|
+
} : null,
|
|
364
|
+
qualificationEvidence,
|
|
365
|
+
requestedActions,
|
|
366
|
+
budget: { estimatedCostUsd, maxCostUsd },
|
|
367
|
+
slaMinutes,
|
|
368
|
+
idempotencyKey: cleanText(input.idempotencyKey) || null,
|
|
369
|
+
requiredEvidence,
|
|
370
|
+
approval: validBusinessApproval(input.approval)
|
|
371
|
+
? { status: 'approved', approvalId: cleanText(input.approval.approvalId) }
|
|
372
|
+
: null,
|
|
373
|
+
};
|
|
374
|
+
const decision = issues.length === 0 ? 'allow' : 'deny';
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
schemaVersion: 'business-function-handoff-evaluation-v1',
|
|
378
|
+
decision,
|
|
379
|
+
issues: Array.from(new Set(issues)),
|
|
380
|
+
contract,
|
|
381
|
+
outcomeReceiptTemplate: decision === 'allow'
|
|
382
|
+
? buildBusinessOutcomeReceiptTemplate(contract)
|
|
383
|
+
: null,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function parseBusinessAgentJson(value, fallback) {
|
|
388
|
+
if (value === undefined || value === null || value === '') return fallback;
|
|
389
|
+
if (typeof value !== 'string') return value;
|
|
390
|
+
try {
|
|
391
|
+
return JSON.parse(value);
|
|
392
|
+
} catch (error) {
|
|
393
|
+
const invalidJson = new Error('Business agent JSON input is invalid.');
|
|
394
|
+
invalidJson.code = 'INVALID_BUSINESS_AGENT_JSON';
|
|
395
|
+
invalidJson.cause = error;
|
|
396
|
+
throw invalidJson;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function buildBusinessFunctionAgentPlan(input = {}) {
|
|
401
|
+
const candidates = parseBusinessAgentJson(input.candidatesJson, input.candidates || []);
|
|
402
|
+
const handoff = parseBusinessAgentJson(input.handoffJson, input.handoff || null);
|
|
403
|
+
const ranking = rankBusinessFunctionAgents(candidates);
|
|
404
|
+
const evaluatedHandoff = handoff ? evaluateBusinessFunctionHandoff(handoff) : null;
|
|
405
|
+
return {
|
|
406
|
+
name: 'thumbgate-business-function-agent-team',
|
|
407
|
+
status: evaluatedHandoff?.decision === 'deny'
|
|
408
|
+
? 'blocked'
|
|
409
|
+
: ranking.status,
|
|
410
|
+
catalog: {
|
|
411
|
+
functions: BUSINESS_FUNCTIONS,
|
|
412
|
+
defaultHandoffEdges: BUSINESS_FUNCTION_HANDOFF_EDGES,
|
|
413
|
+
},
|
|
414
|
+
ranking,
|
|
415
|
+
handoff: evaluatedHandoff,
|
|
416
|
+
nextActions: [
|
|
417
|
+
'Pilot the shortest evidence-backed payback',
|
|
418
|
+
'Gate every cross-function handoff',
|
|
419
|
+
'Record actual KPIs apart from estimates',
|
|
420
|
+
],
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
10
424
|
function buildLoopRoutinePlan(input = {}) {
|
|
11
425
|
const tasks = toList(input.tasks);
|
|
12
426
|
const cadenceMinutes = Number(input.cadenceMinutes || input.cadence || 60);
|
|
@@ -568,6 +982,21 @@ function parseArgs(argv = process.argv.slice(2)) {
|
|
|
568
982
|
return args;
|
|
569
983
|
}
|
|
570
984
|
|
|
985
|
+
function readBusinessTeamOption(argv, name) {
|
|
986
|
+
const prefix = `--${name}=`;
|
|
987
|
+
return argv.find((arg) => arg.startsWith(prefix))?.slice(prefix.length);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
function parseBusinessTeamArgs(argv = process.argv.slice(2)) {
|
|
991
|
+
if (!argv.includes('business-team')) return null;
|
|
992
|
+
return {
|
|
993
|
+
command: 'business-team',
|
|
994
|
+
json: argv.includes('--json'),
|
|
995
|
+
candidatesJson: readBusinessTeamOption(argv, 'candidates-json'),
|
|
996
|
+
handoffJson: readBusinessTeamOption(argv, 'handoff-json'),
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
|
|
571
1000
|
function runCli(args) {
|
|
572
1001
|
const command = args.command || 'loop';
|
|
573
1002
|
const builders = {
|
|
@@ -587,6 +1016,7 @@ function runCli(args) {
|
|
|
587
1016
|
format: buildOutputFormatPlan,
|
|
588
1017
|
'gtm-agency': buildMarketingAgencyGtmPlan,
|
|
589
1018
|
bedrock: buildBedrockAgentCorePlan,
|
|
1019
|
+
'business-team': buildBusinessFunctionAgentPlan,
|
|
590
1020
|
loop: buildLoopRoutinePlan,
|
|
591
1021
|
};
|
|
592
1022
|
const report = (builders[command] || buildLoopRoutinePlan)(args);
|
|
@@ -598,7 +1028,10 @@ function runCli(args) {
|
|
|
598
1028
|
}
|
|
599
1029
|
}
|
|
600
1030
|
|
|
601
|
-
|
|
1031
|
+
// Path-based entrypoint check avoids SonarCloud S3403 on `require.main === module`.
|
|
1032
|
+
if (path.resolve(process.argv[1] || '') === path.resolve(__filename)) {
|
|
1033
|
+
runCli(parseBusinessTeamArgs() || parseArgs());
|
|
1034
|
+
}
|
|
602
1035
|
|
|
603
1036
|
module.exports = {
|
|
604
1037
|
buildLoopRoutinePlan,
|
|
@@ -618,4 +1051,11 @@ module.exports = {
|
|
|
618
1051
|
buildBedrockAgentCorePlan,
|
|
619
1052
|
buildLegalAgentGovernancePlan,
|
|
620
1053
|
buildDynamicWorkflowReadinessPlan,
|
|
1054
|
+
BUSINESS_FUNCTIONS,
|
|
1055
|
+
BUSINESS_FUNCTION_HANDOFF_EDGES,
|
|
1056
|
+
normalizeBusinessFunctionCandidate,
|
|
1057
|
+
rankBusinessFunctionAgents,
|
|
1058
|
+
evaluateBusinessFunctionHandoff,
|
|
1059
|
+
buildBusinessOutcomeReceiptTemplate,
|
|
1060
|
+
buildBusinessFunctionAgentPlan,
|
|
621
1061
|
};
|