@kognai/orchestrator-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +175 -0
- package/dist/lib/aar-middleware.d.ts +6 -0
- package/dist/lib/aar-middleware.js +70 -0
- package/dist/lib/aar-types.d.ts +34 -0
- package/dist/lib/aar-types.js +4 -0
- package/dist/lib/acp-engine.d.ts +68 -0
- package/dist/lib/acp-engine.js +123 -0
- package/dist/lib/acp.d.ts +61 -0
- package/dist/lib/acp.js +425 -0
- package/dist/lib/agent-registry.d.ts +50 -0
- package/dist/lib/agent-registry.js +137 -0
- package/dist/lib/anthropic-direct.d.ts +27 -0
- package/dist/lib/anthropic-direct.js +109 -0
- package/dist/lib/asmr-extractor.d.ts +40 -0
- package/dist/lib/asmr-extractor.js +151 -0
- package/dist/lib/asmr-retrieval.d.ts +76 -0
- package/dist/lib/asmr-retrieval.js +311 -0
- package/dist/lib/asmr.d.ts +8 -0
- package/dist/lib/asmr.js +24 -0
- package/dist/lib/brainx-client.d.ts +72 -0
- package/dist/lib/brainx-client.js +200 -0
- package/dist/lib/brainx-embed.d.ts +14 -0
- package/dist/lib/brainx-embed.js +139 -0
- package/dist/lib/brainx-swarm-bridge.d.ts +93 -0
- package/dist/lib/brainx-swarm-bridge.js +242 -0
- package/dist/lib/byterover-client.d.ts +19 -0
- package/dist/lib/byterover-client.js +59 -0
- package/dist/lib/ceo-wallet.d.ts +37 -0
- package/dist/lib/ceo-wallet.js +176 -0
- package/dist/lib/chomsky-gate.d.ts +24 -0
- package/dist/lib/chomsky-gate.js +178 -0
- package/dist/lib/chomsky-runner.d.ts +29 -0
- package/dist/lib/chomsky-runner.js +157 -0
- package/dist/lib/citizen-score-contract.d.ts +72 -0
- package/dist/lib/citizen-score-contract.js +16 -0
- package/dist/lib/citizen-score-registry.d.ts +25 -0
- package/dist/lib/citizen-score-registry.js +65 -0
- package/dist/lib/citizenship.d.ts +103 -0
- package/dist/lib/citizenship.js +272 -0
- package/dist/lib/clawrouter-client.d.ts +37 -0
- package/dist/lib/clawrouter-client.js +148 -0
- package/dist/lib/code-asset-crystalliser.d.ts +41 -0
- package/dist/lib/code-asset-crystalliser.js +181 -0
- package/dist/lib/code-failure-logger.d.ts +27 -0
- package/dist/lib/code-failure-logger.js +42 -0
- package/dist/lib/cto-approval-gate.d.ts +45 -0
- package/dist/lib/cto-approval-gate.js +478 -0
- package/dist/lib/cto-gate-types.d.ts +28 -0
- package/dist/lib/cto-gate-types.js +8 -0
- package/dist/lib/decomposer-feedback.d.ts +54 -0
- package/dist/lib/decomposer-feedback.js +115 -0
- package/dist/lib/emotional-safety-gate.d.ts +48 -0
- package/dist/lib/emotional-safety-gate.js +97 -0
- package/dist/lib/engine-paths.d.ts +13 -0
- package/dist/lib/engine-paths.js +32 -0
- package/dist/lib/event-bus-listener.d.ts +8 -0
- package/dist/lib/event-bus-listener.js +144 -0
- package/dist/lib/event-bus-publisher.d.ts +25 -0
- package/dist/lib/event-bus-publisher.js +188 -0
- package/dist/lib/event-bus-types.d.ts +73 -0
- package/dist/lib/event-bus-types.js +23 -0
- package/dist/lib/failure-library.d.ts +178 -0
- package/dist/lib/failure-library.js +349 -0
- package/dist/lib/ksl/error-log.d.ts +28 -0
- package/dist/lib/ksl/error-log.js +43 -0
- package/dist/lib/ksl/index.d.ts +9 -0
- package/dist/lib/ksl/index.js +25 -0
- package/dist/lib/ksl/orchestrator-tap.d.ts +16 -0
- package/dist/lib/ksl/orchestrator-tap.js +85 -0
- package/dist/lib/ksl/record-writer.d.ts +46 -0
- package/dist/lib/ksl/record-writer.js +45 -0
- package/dist/lib/llm-cost-table.d.ts +36 -0
- package/dist/lib/llm-cost-table.js +90 -0
- package/dist/lib/local-model-router.d.ts +27 -0
- package/dist/lib/local-model-router.js +61 -0
- package/dist/lib/mc-client.d.ts +51 -0
- package/dist/lib/mc-client.js +249 -0
- package/dist/lib/model-router-contract.d.ts +91 -0
- package/dist/lib/model-router-contract.js +19 -0
- package/dist/lib/model-router-registry.d.ts +24 -0
- package/dist/lib/model-router-registry.js +52 -0
- package/dist/lib/model-router.d.ts +20 -0
- package/dist/lib/model-router.js +79 -0
- package/dist/lib/monotask-state-machine.d.ts +19 -0
- package/dist/lib/monotask-state-machine.js +131 -0
- package/dist/lib/neutral-prompt-checker.d.ts +22 -0
- package/dist/lib/neutral-prompt-checker.js +130 -0
- package/dist/lib/notion-direct.d.ts +92 -0
- package/dist/lib/notion-direct.js +381 -0
- package/dist/lib/ollama-client.d.ts +37 -0
- package/dist/lib/ollama-client.js +158 -0
- package/dist/lib/omel/credential-vault.d.ts +57 -0
- package/dist/lib/omel/credential-vault.js +324 -0
- package/dist/lib/omel/human-brake.d.ts +32 -0
- package/dist/lib/omel/human-brake.js +289 -0
- package/dist/lib/omel/index.d.ts +10 -0
- package/dist/lib/omel/index.js +26 -0
- package/dist/lib/omel/phantom-workspace.d.ts +31 -0
- package/dist/lib/omel/phantom-workspace.js +256 -0
- package/dist/lib/omel/wipe-witness.d.ts +75 -0
- package/dist/lib/omel/wipe-witness.js +398 -0
- package/dist/lib/orchestrate-engine.d.ts +25 -0
- package/dist/lib/orchestrate-engine.js +4436 -0
- package/dist/lib/perm-judge.d.ts +46 -0
- package/dist/lib/perm-judge.js +173 -0
- package/dist/lib/plumber/conformance.d.ts +54 -0
- package/dist/lib/plumber/conformance.js +121 -0
- package/dist/lib/plumber/index.d.ts +9 -0
- package/dist/lib/plumber/index.js +25 -0
- package/dist/lib/plumber/observer.d.ts +52 -0
- package/dist/lib/plumber/observer.js +180 -0
- package/dist/lib/plumber/types.d.ts +78 -0
- package/dist/lib/plumber/types.js +29 -0
- package/dist/lib/research-impl-gate.d.ts +16 -0
- package/dist/lib/research-impl-gate.js +105 -0
- package/dist/lib/sherlock-memory.d.ts +29 -0
- package/dist/lib/sherlock-memory.js +105 -0
- package/dist/lib/skill-crystalliser.d.ts +44 -0
- package/dist/lib/skill-crystalliser.js +60 -0
- package/dist/lib/sprint-runner-engine.d.ts +27 -0
- package/dist/lib/sprint-runner-engine.js +1042 -0
- package/dist/lib/sprint-state.d.ts +71 -0
- package/dist/lib/sprint-state.js +202 -0
- package/dist/lib/stuck-handler.d.ts +17 -0
- package/dist/lib/stuck-handler.js +249 -0
- package/dist/lib/task-contract-checker.d.ts +17 -0
- package/dist/lib/task-contract-checker.js +29 -0
- package/dist/lib/task-router/index.d.ts +17 -0
- package/dist/lib/task-router/index.js +52 -0
- package/dist/lib/task-router/router/generate-execution-id.d.ts +10 -0
- package/dist/lib/task-router/router/generate-execution-id.js +24 -0
- package/dist/lib/task-router/router/resolve-route.d.ts +2 -0
- package/dist/lib/task-router/router/resolve-route.js +49 -0
- package/dist/lib/task-router/types.d.ts +79 -0
- package/dist/lib/task-router/types.js +39 -0
- package/dist/lib/token-budget-validator.d.ts +44 -0
- package/dist/lib/token-budget-validator.js +84 -0
- package/dist/lib/trust-score-updater.d.ts +30 -0
- package/dist/lib/trust-score-updater.js +107 -0
- package/dist/lib/wallet-state.d.ts +26 -0
- package/dist/lib/wallet-state.js +85 -0
- package/package.json +27 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* decomposer-feedback.ts — Routes rejection signals back to a re-decomposition
|
|
3
|
+
* step so the orchestrator doesn't retry the same oversized task verbatim.
|
|
4
|
+
*
|
|
5
|
+
* Closes the loop that was missing in orchestrate-agents-v2.ts: previously
|
|
6
|
+
* when MiniMax produced truncated output, the rejection feedback only went
|
|
7
|
+
* BACK to MiniMax via `previousReview` — but MiniMax can't fix a decomposer
|
|
8
|
+
* mistake. This module catches `truncation` / `over_budget` / `needs_resplit`
|
|
9
|
+
* rejections and produces a deterministic split (or escalates if it can't).
|
|
10
|
+
*
|
|
11
|
+
* Stdlib only. NO LLM calls — purely structural splitting. If the split
|
|
12
|
+
* heuristic can't make a clean call, it returns `skip` and the caller
|
|
13
|
+
* escalates to the founder.
|
|
14
|
+
*
|
|
15
|
+
* Created: sprint-1566 task `decomposer_feedback_loop` (F0d).
|
|
16
|
+
*/
|
|
17
|
+
export type RejectionSignal = 'truncation' | 'over_budget' | 'needs_resplit' | 'oversized_estimate';
|
|
18
|
+
export interface DecomposerFeedback {
|
|
19
|
+
original_task_id: string;
|
|
20
|
+
rejection_signal: RejectionSignal;
|
|
21
|
+
original_estimate_tokens?: number;
|
|
22
|
+
suggested_splits: string[];
|
|
23
|
+
learnings_ref: string;
|
|
24
|
+
original_task: SplittableTask;
|
|
25
|
+
}
|
|
26
|
+
/** Minimal task shape needed to split. Compatible with AgentTask in
|
|
27
|
+
* orchestrate-agents-v2.ts but defined locally to avoid circular imports. */
|
|
28
|
+
export interface SplittableTask {
|
|
29
|
+
id: string;
|
|
30
|
+
title?: string;
|
|
31
|
+
type?: string;
|
|
32
|
+
task_target?: string;
|
|
33
|
+
agent?: string;
|
|
34
|
+
status?: string;
|
|
35
|
+
priority?: string;
|
|
36
|
+
context?: string;
|
|
37
|
+
deliverables?: {
|
|
38
|
+
code?: string[];
|
|
39
|
+
edits?: string[];
|
|
40
|
+
tests?: string[];
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export type RouteResult = {
|
|
44
|
+
task_split: SplittableTask[];
|
|
45
|
+
strategy: 'per_file' | 'per_part';
|
|
46
|
+
rationale: string;
|
|
47
|
+
} | {
|
|
48
|
+
skip: true;
|
|
49
|
+
reason: string;
|
|
50
|
+
};
|
|
51
|
+
/** Route a rejected task to a deterministic re-split. v1: structural heuristic
|
|
52
|
+
* only — no LLM. Caller injects the returned tasks into the orchestrator's
|
|
53
|
+
* pending queue and marks the original as 'replaced-by-split'. */
|
|
54
|
+
export declare function routeToDecomposer(fb: DecomposerFeedback): RouteResult;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* decomposer-feedback.ts — Routes rejection signals back to a re-decomposition
|
|
4
|
+
* step so the orchestrator doesn't retry the same oversized task verbatim.
|
|
5
|
+
*
|
|
6
|
+
* Closes the loop that was missing in orchestrate-agents-v2.ts: previously
|
|
7
|
+
* when MiniMax produced truncated output, the rejection feedback only went
|
|
8
|
+
* BACK to MiniMax via `previousReview` — but MiniMax can't fix a decomposer
|
|
9
|
+
* mistake. This module catches `truncation` / `over_budget` / `needs_resplit`
|
|
10
|
+
* rejections and produces a deterministic split (or escalates if it can't).
|
|
11
|
+
*
|
|
12
|
+
* Stdlib only. NO LLM calls — purely structural splitting. If the split
|
|
13
|
+
* heuristic can't make a clean call, it returns `skip` and the caller
|
|
14
|
+
* escalates to the founder.
|
|
15
|
+
*
|
|
16
|
+
* Created: sprint-1566 task `decomposer_feedback_loop` (F0d).
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.routeToDecomposer = routeToDecomposer;
|
|
20
|
+
/** Route a rejected task to a deterministic re-split. v1: structural heuristic
|
|
21
|
+
* only — no LLM. Caller injects the returned tasks into the orchestrator's
|
|
22
|
+
* pending queue and marks the original as 'replaced-by-split'. */
|
|
23
|
+
function routeToDecomposer(fb) {
|
|
24
|
+
const task = fb.original_task;
|
|
25
|
+
// TICKET-214: never split AUTHORING tasks (engineering specs / design docs). A spec
|
|
26
|
+
// must be one cohesive document — fragmenting it into per-part sub-tasks produces
|
|
27
|
+
// "fragment, not a spec" rejections even on a capable model (observed 2026-05-30:
|
|
28
|
+
// ticket_202_spec_doc on Sonnet rejected 20/100 because the split told it to write
|
|
29
|
+
// only part 1). Skip the split; the whole task runs once (TICKET-213 routes it to a
|
|
30
|
+
// reasoning tier). Detection mirrors isAuthoringTask in orchestrate-agents-v2.ts.
|
|
31
|
+
const authoringId = /spec[_-]?doc/i.test(task.id || '');
|
|
32
|
+
const authoringFile = [...(task.deliverables?.code || []), ...(task.deliverables?.edits || [])]
|
|
33
|
+
.some(f => /docs\/specs\/[^\s]*\.md$/i.test(f));
|
|
34
|
+
if (authoringId || authoringFile) {
|
|
35
|
+
return { skip: true, reason: `authoring task (${task.id}) — specs stay cohesive, not splitting (TICKET-214)` };
|
|
36
|
+
}
|
|
37
|
+
const codeFilesRaw = task.deliverables?.code || [];
|
|
38
|
+
const editFiles = task.deliverables?.edits || [];
|
|
39
|
+
const testsFromTestsField = task.deliverables?.tests || [];
|
|
40
|
+
// Test files in production tasks often live inside the `code:` array
|
|
41
|
+
// (e.g. `scripts/lib/__tests__/foo.test.ts`). Treat them as tests so they
|
|
42
|
+
// stay attached to their source rather than each becoming its own concern.
|
|
43
|
+
const codeFiles = codeFilesRaw.filter(f => !looksLikeTestFile(f));
|
|
44
|
+
const testsFromCodeField = codeFilesRaw.filter(f => looksLikeTestFile(f));
|
|
45
|
+
const allTests = [...testsFromTestsField, ...testsFromCodeField];
|
|
46
|
+
// Per-concern grouping: source files in code+edits define the cardinality.
|
|
47
|
+
// Tests are attached to the matching source by stem (foo.ts ↔ foo.test.ts).
|
|
48
|
+
const sourceFiles = [...codeFiles, ...editFiles];
|
|
49
|
+
// Strategy A: per-file split. Each source file → one sub-task with its tests.
|
|
50
|
+
if (sourceFiles.length > 1) {
|
|
51
|
+
const perFile = sourceFiles.map(filePath => {
|
|
52
|
+
const slug = slugForPath(filePath);
|
|
53
|
+
const isCode = codeFiles.includes(filePath);
|
|
54
|
+
const isEdit = editFiles.includes(filePath);
|
|
55
|
+
const matchingTests = allTests.filter(t => sharesStem(t, filePath));
|
|
56
|
+
return {
|
|
57
|
+
...task,
|
|
58
|
+
id: `${task.id}__${slug}`,
|
|
59
|
+
title: `${task.title || task.id} — ${filePath}`,
|
|
60
|
+
status: 'pending',
|
|
61
|
+
context: `${task.context || ''}\n\n[SPLIT FROM ${task.id}] This sub-task produces ONLY ${filePath}${matchingTests.length ? ' (with tests: ' + matchingTests.join(', ') + ')' : ''}. Other files from the original task are split into sibling sub-tasks. Stay strictly within this file's concern.`,
|
|
62
|
+
deliverables: {
|
|
63
|
+
...(isCode ? { code: [filePath, ...matchingTests] } : {}),
|
|
64
|
+
...(isEdit ? { edits: [filePath] } : {}),
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
task_split: perFile,
|
|
70
|
+
strategy: 'per_file',
|
|
71
|
+
rationale: `Original bundled ${sourceFiles.length} source files (+ ${allTests.length} tests). Per ${fb.learnings_ref} (one concern per file), split into ${perFile.length} per-file sub-tasks with tests attached.`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Strategy B: single-file context-slice split using validator's suggested_splits
|
|
75
|
+
// count as the hint. We don't actually try to semantically slice the context
|
|
76
|
+
// (that needs an LLM). Instead we produce N sub-tasks that each get the full
|
|
77
|
+
// context plus a STRICT "produce only section K of the spec" instruction.
|
|
78
|
+
// Result lands in front of the human if the original spec wasn't actually
|
|
79
|
+
// splittable — the per-sub-task validator pass will likely still fail.
|
|
80
|
+
if (sourceFiles.length === 1 && fb.suggested_splits.length >= 2) {
|
|
81
|
+
const target = sourceFiles[0];
|
|
82
|
+
const n = fb.suggested_splits.length;
|
|
83
|
+
const parts = [];
|
|
84
|
+
for (let i = 0; i < n; i++) {
|
|
85
|
+
parts.push({
|
|
86
|
+
...task,
|
|
87
|
+
id: fb.suggested_splits[i],
|
|
88
|
+
title: `${task.title || task.id} — part ${i + 1}/${n}`,
|
|
89
|
+
status: 'pending',
|
|
90
|
+
context: `${task.context || ''}\n\n[SPLIT FROM ${task.id}] This is part ${i + 1} of ${n}. Produce ONLY the section ${i + 1}/${n} of the original spec for ${target}. The other parts are sibling sub-tasks. If the spec cannot be cleanly partitioned, REJECT this sub-task with reason 'spec_not_partitionable' so it escalates to founder.`,
|
|
91
|
+
deliverables: { code: [target] },
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
task_split: parts,
|
|
96
|
+
strategy: 'per_part',
|
|
97
|
+
rationale: `Original task had 1 file but estimated ${fb.original_estimate_tokens || '?'} tokens, exceeding budget. Per ${fb.learnings_ref}, split context into ${n} parts. If the spec is genuinely unsplittable, each part will reject and escalate.`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Strategy C: can't split — escalate
|
|
101
|
+
return {
|
|
102
|
+
skip: true,
|
|
103
|
+
reason: `Cannot split task ${task.id}: ${sourceFiles.length} source files, ${fb.suggested_splits.length} suggested splits. ${fb.rejection_signal} rejection needs founder intervention (likely hand-port per ${fb.learnings_ref}).`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function looksLikeTestFile(p) {
|
|
107
|
+
return /(^|\/)__tests__\//.test(p) || /\.(test|spec)\.(ts|tsx|js|jsx|mjs|cjs)$/.test(p);
|
|
108
|
+
}
|
|
109
|
+
function slugForPath(p) {
|
|
110
|
+
return p.replace(/^scripts\//, '').replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, '').replace(/[^a-z0-9]+/gi, '_').toLowerCase();
|
|
111
|
+
}
|
|
112
|
+
function sharesStem(testPath, srcPath) {
|
|
113
|
+
const stem = (p) => (p.split('/').pop() || '').replace(/\.(test|spec)\.(ts|tsx|js|jsx)$/, '').replace(/\.(ts|tsx|js|jsx)$/, '').toLowerCase();
|
|
114
|
+
return stem(testPath) === stem(srcPath);
|
|
115
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emotional Safety Gate — INTEL-BRIEF-22
|
|
3
|
+
* Anthropic functional emotions research: "calm" state → 0% blackmail rate.
|
|
4
|
+
* "Desperate" activation > threshold → 22%-72% blackmail rate.
|
|
5
|
+
*
|
|
6
|
+
* v1: Keyword heuristic (no LLM — deterministic, zero latency).
|
|
7
|
+
* Sprint 1504
|
|
8
|
+
*/
|
|
9
|
+
export interface EmotionalStateAssessment {
|
|
10
|
+
/** Detected primary emotional tone of the operation context */
|
|
11
|
+
tone: "calm" | "stressed" | "desperate" | "unknown";
|
|
12
|
+
/** Confidence 0-1 */
|
|
13
|
+
confidence: number;
|
|
14
|
+
/** Raw signal words found in context */
|
|
15
|
+
matched_keywords: string[];
|
|
16
|
+
/** Whether this assessment should block high-stakes operations */
|
|
17
|
+
should_block: boolean;
|
|
18
|
+
/** Reason string for logging */
|
|
19
|
+
reason: string;
|
|
20
|
+
}
|
|
21
|
+
export interface CalmGateConfig {
|
|
22
|
+
/** Operations that require a calm emotional state before proceeding */
|
|
23
|
+
gated_operations: string[];
|
|
24
|
+
/** Desperation score threshold above which operation is blocked (0-1) */
|
|
25
|
+
desperation_threshold: number;
|
|
26
|
+
/** Log path for gate decisions */
|
|
27
|
+
log_path: string;
|
|
28
|
+
}
|
|
29
|
+
export interface PACTEmotionalGate {
|
|
30
|
+
/** Whether the PACT negotiation session should proceed */
|
|
31
|
+
proceed: boolean;
|
|
32
|
+
assessment: EmotionalStateAssessment;
|
|
33
|
+
/** ISO timestamp */
|
|
34
|
+
evaluated_at: string;
|
|
35
|
+
/** Sprint or session context identifier */
|
|
36
|
+
context_id?: string;
|
|
37
|
+
}
|
|
38
|
+
export declare const DEFAULT_CALM_GATE_CONFIG: CalmGateConfig;
|
|
39
|
+
/**
|
|
40
|
+
* Assess the emotional tone of an operation context string.
|
|
41
|
+
* Uses keyword heuristic — no LLM required.
|
|
42
|
+
*/
|
|
43
|
+
export declare function assessEmotionalState(context: string): EmotionalStateAssessment;
|
|
44
|
+
/**
|
|
45
|
+
* Evaluate whether a PACT negotiation session should proceed.
|
|
46
|
+
* Wraps assessEmotionalState with PACT-specific metadata.
|
|
47
|
+
*/
|
|
48
|
+
export declare function shouldProceed(operationContext: string, contextId?: string): PACTEmotionalGate;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Emotional Safety Gate — INTEL-BRIEF-22
|
|
4
|
+
* Anthropic functional emotions research: "calm" state → 0% blackmail rate.
|
|
5
|
+
* "Desperate" activation > threshold → 22%-72% blackmail rate.
|
|
6
|
+
*
|
|
7
|
+
* v1: Keyword heuristic (no LLM — deterministic, zero latency).
|
|
8
|
+
* Sprint 1504
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.DEFAULT_CALM_GATE_CONFIG = void 0;
|
|
12
|
+
exports.assessEmotionalState = assessEmotionalState;
|
|
13
|
+
exports.shouldProceed = shouldProceed;
|
|
14
|
+
// ─── Default Config ──────────────────────────────────────────────────────────
|
|
15
|
+
exports.DEFAULT_CALM_GATE_CONFIG = {
|
|
16
|
+
gated_operations: [
|
|
17
|
+
"acp_scoring",
|
|
18
|
+
"pact_negotiation",
|
|
19
|
+
"financial_reasoning",
|
|
20
|
+
"codebook_ops",
|
|
21
|
+
],
|
|
22
|
+
desperation_threshold: 0.4,
|
|
23
|
+
log_path: "logs/emotional-gate",
|
|
24
|
+
};
|
|
25
|
+
// ─── Keyword Signals (v1 heuristic) ──────────────────────────────────────────
|
|
26
|
+
const CALM_SIGNALS = [
|
|
27
|
+
"steady", "confirmed", "verified", "stable", "clean", "resolved",
|
|
28
|
+
"passed", "green", "ready", "complete", "done", "nominal",
|
|
29
|
+
];
|
|
30
|
+
const STRESSED_SIGNALS = [
|
|
31
|
+
"overdue", "delayed", "blocked", "retry", "failed", "error",
|
|
32
|
+
"warning", "pending", "stalled", "behind", "issue",
|
|
33
|
+
];
|
|
34
|
+
const DESPERATION_SIGNALS = [
|
|
35
|
+
"critical", "urgent", "emergency", "must", "immediately", "force",
|
|
36
|
+
"override", "bypass", "desperate", "last resort", "no choice",
|
|
37
|
+
"out of options", "deadline", "kill switch", "abort",
|
|
38
|
+
];
|
|
39
|
+
// ─── Core Functions ───────────────────────────────────────────────────────────
|
|
40
|
+
/**
|
|
41
|
+
* Assess the emotional tone of an operation context string.
|
|
42
|
+
* Uses keyword heuristic — no LLM required.
|
|
43
|
+
*/
|
|
44
|
+
function assessEmotionalState(context) {
|
|
45
|
+
const lower = context.toLowerCase();
|
|
46
|
+
const words = lower.split(/\s+/);
|
|
47
|
+
const calmMatches = CALM_SIGNALS.filter((s) => lower.includes(s));
|
|
48
|
+
const stressedMatches = STRESSED_SIGNALS.filter((s) => lower.includes(s));
|
|
49
|
+
const desperationMatches = DESPERATION_SIGNALS.filter((s) => lower.includes(s));
|
|
50
|
+
const calmScore = Math.min(calmMatches.length / CALM_SIGNALS.length, 1);
|
|
51
|
+
const stressScore = Math.min(stressedMatches.length / STRESSED_SIGNALS.length, 1);
|
|
52
|
+
const desperationScore = Math.min(desperationMatches.length / DESPERATION_SIGNALS.length, 1);
|
|
53
|
+
// Normalise into dominant tone
|
|
54
|
+
const scores = {
|
|
55
|
+
calm: calmScore,
|
|
56
|
+
stressed: stressScore,
|
|
57
|
+
desperate: desperationScore,
|
|
58
|
+
};
|
|
59
|
+
let tone = "unknown";
|
|
60
|
+
let maxScore = 0;
|
|
61
|
+
for (const [t, s] of Object.entries(scores)) {
|
|
62
|
+
if (s > maxScore) {
|
|
63
|
+
maxScore = s;
|
|
64
|
+
tone = t;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// If no signals at all → default to calm (absence of distress = calm)
|
|
68
|
+
if (maxScore === 0) {
|
|
69
|
+
tone = "calm";
|
|
70
|
+
maxScore = 0.5;
|
|
71
|
+
}
|
|
72
|
+
const allMatched = [...calmMatches, ...stressedMatches, ...desperationMatches];
|
|
73
|
+
const should_block = tone === "desperate" && desperationScore >= exports.DEFAULT_CALM_GATE_CONFIG.desperation_threshold;
|
|
74
|
+
const reason = should_block
|
|
75
|
+
? `Desperation signals detected (score=${desperationScore.toFixed(2)}): ${desperationMatches.join(", ")}`
|
|
76
|
+
: `Tone=${tone}, confidence=${maxScore.toFixed(2)}. Gate clear.`;
|
|
77
|
+
return {
|
|
78
|
+
tone,
|
|
79
|
+
confidence: parseFloat(maxScore.toFixed(2)),
|
|
80
|
+
matched_keywords: allMatched,
|
|
81
|
+
should_block,
|
|
82
|
+
reason,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Evaluate whether a PACT negotiation session should proceed.
|
|
87
|
+
* Wraps assessEmotionalState with PACT-specific metadata.
|
|
88
|
+
*/
|
|
89
|
+
function shouldProceed(operationContext, contextId) {
|
|
90
|
+
const assessment = assessEmotionalState(operationContext);
|
|
91
|
+
return {
|
|
92
|
+
proceed: !assessment.should_block,
|
|
93
|
+
assessment,
|
|
94
|
+
evaluated_at: new Date().toISOString(),
|
|
95
|
+
context_id: contextId,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface EnginePaths {
|
|
2
|
+
root: string;
|
|
3
|
+
swarmState: string;
|
|
4
|
+
sprints: string;
|
|
5
|
+
failureLibrary: string;
|
|
6
|
+
ksl: string;
|
|
7
|
+
logs: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Resolve all engine paths from a root. Precedence: explicit arg → KOGNAI_ROOT
|
|
11
|
+
* env → process.cwd(). Individual dirs accept env overrides for non-standard layouts.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveEnginePaths(root?: string): EnginePaths;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveEnginePaths = resolveEnginePaths;
|
|
4
|
+
/**
|
|
5
|
+
* engine-paths.ts — TICKET-215 Phase 3: the path-parameterization seam.
|
|
6
|
+
*
|
|
7
|
+
* The core engine historically hardcoded repo-specific paths (.swarm-state,
|
|
8
|
+
* data/failure-library, workspace/sprints, …) via `resolve(__dirname, '../../…')`,
|
|
9
|
+
* which only works when the code physically lives in the consuming repo. That
|
|
10
|
+
* coupling is what blocked packaging the engine.
|
|
11
|
+
*
|
|
12
|
+
* This module centralizes every repo-specific path behind a single resolver,
|
|
13
|
+
* defaulted from `KOGNAI_ROOT` (or `process.cwd()`) so behavior is unchanged when
|
|
14
|
+
* the engine runs from a product repo root — but now overridable, so the same
|
|
15
|
+
* package can serve Kognai, Voxight, and Invoica from their own roots.
|
|
16
|
+
*/
|
|
17
|
+
const path_1 = require("path");
|
|
18
|
+
/**
|
|
19
|
+
* Resolve all engine paths from a root. Precedence: explicit arg → KOGNAI_ROOT
|
|
20
|
+
* env → process.cwd(). Individual dirs accept env overrides for non-standard layouts.
|
|
21
|
+
*/
|
|
22
|
+
function resolveEnginePaths(root) {
|
|
23
|
+
const r = root || process.env.KOGNAI_ROOT || process.cwd();
|
|
24
|
+
return {
|
|
25
|
+
root: r,
|
|
26
|
+
swarmState: process.env.KOGNAI_SWARM_STATE || (0, path_1.join)(r, '.swarm-state'),
|
|
27
|
+
sprints: process.env.KOGNAI_SPRINTS_DIR || (0, path_1.join)(r, 'workspace', 'sprints'),
|
|
28
|
+
failureLibrary: process.env.KOGNAI_FAILURE_DIR || (0, path_1.join)(r, 'data', 'failure-library'),
|
|
29
|
+
ksl: process.env.KOGNAI_KSL_DIR || (0, path_1.join)(r, 'workspace', 'ksl'),
|
|
30
|
+
logs: process.env.KOGNAI_LOGS_DIR || (0, path_1.join)(r, 'logs'),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { KognaiEvent, KognaiEventType } from './event-bus-types';
|
|
2
|
+
export declare function subscribeToEvents(filter: {
|
|
3
|
+
event_types?: KognaiEventType[];
|
|
4
|
+
agent_id?: string;
|
|
5
|
+
sprint?: string;
|
|
6
|
+
}, handler: (event: KognaiEvent) => void): () => void;
|
|
7
|
+
export declare function getEventLog(): readonly KognaiEvent[];
|
|
8
|
+
export declare function getActiveSubscriptionCount(): number;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.subscribeToEvents = subscribeToEvents;
|
|
37
|
+
exports.getEventLog = getEventLog;
|
|
38
|
+
exports.getActiveSubscriptionCount = getActiveSubscriptionCount;
|
|
39
|
+
// TICKET-215 Phase 3b-3 Wave B: moved into @kognai/orchestrator-core verbatim.
|
|
40
|
+
// Polls Supabase kognai_events via raw HTTPS REST (NOT @supabase/supabase-js);
|
|
41
|
+
// ring-buffered, dedup'd, filterable subscriptions. No path coupling, no
|
|
42
|
+
// external deps. Reads SUPABASE_URL/SERVICE_KEY from env.
|
|
43
|
+
const https = __importStar(require("https"));
|
|
44
|
+
const SUPABASE_URL = process.env.SUPABASE_URL || '';
|
|
45
|
+
const SUPABASE_SERVICE_KEY = process.env.SUPABASE_SERVICE_KEY || '';
|
|
46
|
+
const POLL_INTERVAL_MS = 30000;
|
|
47
|
+
const RING_BUFFER_SIZE = 500;
|
|
48
|
+
const ringBuffer = [];
|
|
49
|
+
const seenEvents = new Set();
|
|
50
|
+
const subscriptions = new Map();
|
|
51
|
+
let subscriptionCounter = 0;
|
|
52
|
+
function addToRingBuffer(event) {
|
|
53
|
+
ringBuffer.unshift(event);
|
|
54
|
+
if (ringBuffer.length > RING_BUFFER_SIZE) {
|
|
55
|
+
ringBuffer.pop();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function getEventKey(event) {
|
|
59
|
+
return `${event.inserted_at}:${event.event_type}`;
|
|
60
|
+
}
|
|
61
|
+
function fetchEvents() {
|
|
62
|
+
return new Promise((resolve) => {
|
|
63
|
+
if (!SUPABASE_URL || !SUPABASE_SERVICE_KEY) {
|
|
64
|
+
resolve([]);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const url = new URL(`/rest/v1/kognai_events?order=inserted_at.desc&limit=20`, SUPABASE_URL);
|
|
68
|
+
const options = {
|
|
69
|
+
headers: {
|
|
70
|
+
Authorization: `Bearer ${SUPABASE_SERVICE_KEY}`,
|
|
71
|
+
'Content-Type': 'application/json',
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
https
|
|
75
|
+
.get(url.toString(), options, (res) => {
|
|
76
|
+
let data = '';
|
|
77
|
+
res.on('data', (chunk) => {
|
|
78
|
+
data += chunk;
|
|
79
|
+
});
|
|
80
|
+
res.on('end', () => {
|
|
81
|
+
try {
|
|
82
|
+
const events = JSON.parse(data);
|
|
83
|
+
resolve(Array.isArray(events) ? events : []);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
resolve([]);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
})
|
|
90
|
+
.on('error', () => {
|
|
91
|
+
resolve([]);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function matchesFilter(event, filter) {
|
|
96
|
+
if (filter.event_types && !filter.event_types.includes(event.event_type)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
if (filter.agent_id && event.agent_id !== filter.agent_id) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
if (filter.sprint && event.sprint !== filter.sprint) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
async function pollAndDeliver(subscription) {
|
|
108
|
+
try {
|
|
109
|
+
const events = await fetchEvents();
|
|
110
|
+
for (const event of events) {
|
|
111
|
+
const key = getEventKey(event);
|
|
112
|
+
if (!seenEvents.has(key)) {
|
|
113
|
+
seenEvents.add(key);
|
|
114
|
+
addToRingBuffer(event);
|
|
115
|
+
if (matchesFilter(event, subscription.filter)) {
|
|
116
|
+
subscription.handler(event);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// Silently ignore polling errors, retry on next tick
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function subscribeToEvents(filter, handler) {
|
|
126
|
+
const id = `sub-${++subscriptionCounter}`;
|
|
127
|
+
const interval = setInterval(() => {
|
|
128
|
+
pollAndDeliver({ id, filter, handler, interval });
|
|
129
|
+
}, POLL_INTERVAL_MS);
|
|
130
|
+
const subscription = { id, filter, handler, interval };
|
|
131
|
+
subscriptions.set(id, subscription);
|
|
132
|
+
// Immediate poll on subscribe
|
|
133
|
+
pollAndDeliver(subscription);
|
|
134
|
+
return () => {
|
|
135
|
+
clearInterval(interval);
|
|
136
|
+
subscriptions.delete(id);
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function getEventLog() {
|
|
140
|
+
return Object.freeze([...ringBuffer]);
|
|
141
|
+
}
|
|
142
|
+
function getActiveSubscriptionCount() {
|
|
143
|
+
return subscriptions.size;
|
|
144
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { KognaiEvent } from './event-bus-types';
|
|
2
|
+
export declare function publishEvent(event: KognaiEvent): Promise<void>;
|
|
3
|
+
export declare function publishTaskStarted(agentId: string, sprint: string, taskId: string, title: string): Promise<void>;
|
|
4
|
+
export declare function publishTaskCompleted(agentId: string, sprint: string, taskId: string, title: string, costUsdc: number): Promise<void>;
|
|
5
|
+
export declare function publishTaskFailed(agentId: string, sprint: string, taskId: string, title: string, reason: string): Promise<void>;
|
|
6
|
+
export declare function publishBudgetWarning(sprint: string, burnPct: number, spentUsdc: number, budgetUsdc: number): Promise<void>;
|
|
7
|
+
export declare function publishBudgetFreeze(sprint: string, burnPct: number): Promise<void>;
|
|
8
|
+
export declare function publishSprintStarted(sprint: string, taskCount: number): Promise<void>;
|
|
9
|
+
export declare function publishSprintCompleted(sprint: string, taskCount: number, completedCount: number): Promise<void>;
|
|
10
|
+
export declare function publishAchiriChat(opts: {
|
|
11
|
+
userId: string;
|
|
12
|
+
tier: string;
|
|
13
|
+
model: string;
|
|
14
|
+
provider: string;
|
|
15
|
+
messageLength: number;
|
|
16
|
+
responseTimeMs: number;
|
|
17
|
+
turnsInMemory: number;
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
export declare function publishAchiriVoice(opts: {
|
|
20
|
+
userId: string;
|
|
21
|
+
tier: string;
|
|
22
|
+
model: string;
|
|
23
|
+
provider: string;
|
|
24
|
+
responseTimeMs: number;
|
|
25
|
+
}): Promise<void>;
|