iranti 0.2.51 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -17
- package/dist/scripts/api-key-create.js +1 -1
- package/dist/scripts/api-key-list.js +1 -1
- package/dist/scripts/api-key-revoke.js +1 -1
- package/dist/scripts/claude-code-memory-hook.js +116 -30
- package/dist/scripts/codex-setup.js +86 -4
- package/dist/scripts/iranti-cli.js +1359 -57
- package/dist/scripts/iranti-mcp.js +578 -75
- package/dist/scripts/seed.js +11 -6
- package/dist/scripts/setup.js +1 -1
- package/dist/src/api/healthChecks.d.ts +29 -0
- package/dist/src/api/healthChecks.d.ts.map +1 -0
- package/dist/src/api/healthChecks.js +72 -0
- package/dist/src/api/healthChecks.js.map +1 -0
- package/dist/src/api/middleware/validation.d.ts +22 -0
- package/dist/src/api/middleware/validation.d.ts.map +1 -1
- package/dist/src/api/middleware/validation.js +93 -3
- package/dist/src/api/middleware/validation.js.map +1 -1
- package/dist/src/api/routes/knowledge.d.ts.map +1 -1
- package/dist/src/api/routes/knowledge.js +53 -0
- package/dist/src/api/routes/knowledge.js.map +1 -1
- package/dist/src/api/routes/memory.d.ts.map +1 -1
- package/dist/src/api/routes/memory.js +73 -9
- package/dist/src/api/routes/memory.js.map +1 -1
- package/dist/src/api/server.js +38 -43
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/attendant/AttendantInstance.d.ts +135 -2
- package/dist/src/attendant/AttendantInstance.d.ts.map +1 -1
- package/dist/src/attendant/AttendantInstance.js +1836 -93
- package/dist/src/attendant/AttendantInstance.js.map +1 -1
- package/dist/src/attendant/index.d.ts +1 -1
- package/dist/src/attendant/index.d.ts.map +1 -1
- package/dist/src/attendant/index.js +1 -1
- package/dist/src/attendant/index.js.map +1 -1
- package/dist/src/attendant/registry.d.ts.map +1 -1
- package/dist/src/attendant/registry.js +2 -0
- package/dist/src/attendant/registry.js.map +1 -1
- package/dist/src/chat/index.d.ts +23 -0
- package/dist/src/chat/index.d.ts.map +1 -1
- package/dist/src/chat/index.js +111 -22
- package/dist/src/chat/index.js.map +1 -1
- package/dist/src/generated/prisma/browser.d.ts +5 -0
- package/dist/src/generated/prisma/browser.d.ts.map +1 -1
- package/dist/src/generated/prisma/client.d.ts +5 -0
- package/dist/src/generated/prisma/client.d.ts.map +1 -1
- package/dist/src/generated/prisma/commonInputTypes.d.ts +48 -0
- package/dist/src/generated/prisma/commonInputTypes.d.ts.map +1 -1
- package/dist/src/generated/prisma/internal/class.d.ts +11 -0
- package/dist/src/generated/prisma/internal/class.d.ts.map +1 -1
- package/dist/src/generated/prisma/internal/class.js +4 -4
- package/dist/src/generated/prisma/internal/class.js.map +1 -1
- package/dist/src/generated/prisma/internal/prismaNamespace.d.ts +92 -1
- package/dist/src/generated/prisma/internal/prismaNamespace.d.ts.map +1 -1
- package/dist/src/generated/prisma/internal/prismaNamespace.js +17 -2
- package/dist/src/generated/prisma/internal/prismaNamespace.js.map +1 -1
- package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.d.ts +16 -0
- package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.d.ts.map +1 -1
- package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.js +17 -2
- package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.js.map +1 -1
- package/dist/src/generated/prisma/models/StaffEvent.d.ts +1184 -0
- package/dist/src/generated/prisma/models/StaffEvent.d.ts.map +1 -0
- package/dist/src/generated/prisma/models/StaffEvent.js +3 -0
- package/dist/src/generated/prisma/models/StaffEvent.js.map +1 -0
- package/dist/src/generated/prisma/models.d.ts +1 -0
- package/dist/src/generated/prisma/models.d.ts.map +1 -1
- package/dist/src/lib/assistantCheckpoint.d.ts +21 -0
- package/dist/src/lib/assistantCheckpoint.d.ts.map +1 -0
- package/dist/src/lib/assistantCheckpoint.js +143 -0
- package/dist/src/lib/assistantCheckpoint.js.map +1 -0
- package/dist/src/lib/autoRemember.d.ts +15 -0
- package/dist/src/lib/autoRemember.d.ts.map +1 -1
- package/dist/src/lib/autoRemember.js +433 -71
- package/dist/src/lib/autoRemember.js.map +1 -1
- package/dist/src/lib/cliHelpCatalog.d.ts.map +1 -1
- package/dist/src/lib/cliHelpCatalog.js +23 -11
- package/dist/src/lib/cliHelpCatalog.js.map +1 -1
- package/dist/src/lib/cliHelpRenderer.d.ts +1 -0
- package/dist/src/lib/cliHelpRenderer.d.ts.map +1 -1
- package/dist/src/lib/cliHelpRenderer.js +4 -0
- package/dist/src/lib/cliHelpRenderer.js.map +1 -1
- package/dist/src/lib/commandErrors.d.ts +5 -1
- package/dist/src/lib/commandErrors.d.ts.map +1 -1
- package/dist/src/lib/commandErrors.js +250 -17
- package/dist/src/lib/commandErrors.js.map +1 -1
- package/dist/src/lib/createFirstPartyIranti.d.ts.map +1 -1
- package/dist/src/lib/createFirstPartyIranti.js +1 -0
- package/dist/src/lib/createFirstPartyIranti.js.map +1 -1
- package/dist/src/lib/dbStaffEventEmitter.d.ts +2 -0
- package/dist/src/lib/dbStaffEventEmitter.d.ts.map +1 -1
- package/dist/src/lib/dbStaffEventEmitter.js +15 -0
- package/dist/src/lib/dbStaffEventEmitter.js.map +1 -1
- package/dist/src/lib/hostMemoryFormatting.d.ts +25 -0
- package/dist/src/lib/hostMemoryFormatting.d.ts.map +1 -0
- package/dist/src/lib/hostMemoryFormatting.js +55 -0
- package/dist/src/lib/hostMemoryFormatting.js.map +1 -0
- package/dist/src/lib/issueFacts.d.ts +37 -0
- package/dist/src/lib/issueFacts.d.ts.map +1 -0
- package/dist/src/lib/issueFacts.js +72 -0
- package/dist/src/lib/issueFacts.js.map +1 -0
- package/dist/src/lib/llm.d.ts +8 -0
- package/dist/src/lib/llm.d.ts.map +1 -1
- package/dist/src/lib/llm.js +33 -0
- package/dist/src/lib/llm.js.map +1 -1
- package/dist/src/lib/packageRoot.d.ts +2 -0
- package/dist/src/lib/packageRoot.d.ts.map +1 -0
- package/dist/src/lib/packageRoot.js +22 -0
- package/dist/src/lib/packageRoot.js.map +1 -0
- package/dist/src/lib/projectLearning.d.ts +21 -0
- package/dist/src/lib/projectLearning.d.ts.map +1 -0
- package/dist/src/lib/projectLearning.js +357 -0
- package/dist/src/lib/projectLearning.js.map +1 -0
- package/dist/src/lib/protocolEnforcement.d.ts +29 -0
- package/dist/src/lib/protocolEnforcement.d.ts.map +1 -0
- package/dist/src/lib/protocolEnforcement.js +124 -0
- package/dist/src/lib/protocolEnforcement.js.map +1 -0
- package/dist/src/lib/providers/claude.js +1 -1
- package/dist/src/lib/providers/claude.js.map +1 -1
- package/dist/src/lib/router.js +1 -1
- package/dist/src/lib/router.js.map +1 -1
- package/dist/src/lib/runtimeEnv.d.ts.map +1 -1
- package/dist/src/lib/runtimeEnv.js +8 -3
- package/dist/src/lib/runtimeEnv.js.map +1 -1
- package/dist/src/lib/scaffoldCloseout.d.ts +27 -0
- package/dist/src/lib/scaffoldCloseout.d.ts.map +1 -0
- package/dist/src/lib/scaffoldCloseout.js +139 -0
- package/dist/src/lib/scaffoldCloseout.js.map +1 -0
- package/dist/src/lib/semanticFactTags.d.ts +10 -0
- package/dist/src/lib/semanticFactTags.d.ts.map +1 -0
- package/dist/src/lib/semanticFactTags.js +166 -0
- package/dist/src/lib/semanticFactTags.js.map +1 -0
- package/dist/src/lib/sessionLedger.d.ts +94 -0
- package/dist/src/lib/sessionLedger.d.ts.map +1 -0
- package/dist/src/lib/sessionLedger.js +997 -0
- package/dist/src/lib/sessionLedger.js.map +1 -0
- package/dist/src/lib/sharedStateInvalidation.d.ts +10 -0
- package/dist/src/lib/sharedStateInvalidation.d.ts.map +1 -0
- package/dist/src/lib/sharedStateInvalidation.js +184 -0
- package/dist/src/lib/sharedStateInvalidation.js.map +1 -0
- package/dist/src/lib/staffEventsTable.d.ts +3 -0
- package/dist/src/lib/staffEventsTable.d.ts.map +1 -0
- package/dist/src/lib/staffEventsTable.js +58 -0
- package/dist/src/lib/staffEventsTable.js.map +1 -0
- package/dist/src/librarian/index.d.ts.map +1 -1
- package/dist/src/librarian/index.js +113 -2
- package/dist/src/librarian/index.js.map +1 -1
- package/dist/src/library/client.d.ts +6 -1
- package/dist/src/library/client.d.ts.map +1 -1
- package/dist/src/library/client.js +21 -7
- package/dist/src/library/client.js.map +1 -1
- package/dist/src/library/embeddings.d.ts +9 -1
- package/dist/src/library/embeddings.d.ts.map +1 -1
- package/dist/src/library/embeddings.js +28 -3
- package/dist/src/library/embeddings.js.map +1 -1
- package/dist/src/library/queries.d.ts.map +1 -1
- package/dist/src/library/queries.js +263 -46
- package/dist/src/library/queries.js.map +1 -1
- package/dist/src/sdk/index.d.ts +52 -1
- package/dist/src/sdk/index.d.ts.map +1 -1
- package/dist/src/sdk/index.js +546 -98
- package/dist/src/sdk/index.js.map +1 -1
- package/package.json +24 -3
- package/prisma/migrations/20260331101500_add_staff_events_ledger/migration.sql +24 -0
- package/prisma/schema.prisma +22 -0
|
@@ -32,15 +32,47 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
35
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
40
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
36
41
|
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
37
42
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
38
43
|
const z = __importStar(require("zod/v4"));
|
|
44
|
+
const sdk_1 = require("../src/sdk");
|
|
39
45
|
const commandErrors_1 = require("../src/lib/commandErrors");
|
|
40
46
|
const createFirstPartyIranti_1 = require("../src/lib/createFirstPartyIranti");
|
|
41
47
|
const runtimeEnv_1 = require("../src/lib/runtimeEnv");
|
|
42
48
|
const autoRemember_1 = require("../src/lib/autoRemember");
|
|
49
|
+
const staffEventRegistry_1 = require("../src/lib/staffEventRegistry");
|
|
50
|
+
const client_1 = require("../src/library/client");
|
|
51
|
+
const attendant_1 = require("../src/attendant");
|
|
52
|
+
const assistantCheckpoint_1 = require("../src/lib/assistantCheckpoint");
|
|
53
|
+
const hostMemoryFormatting_1 = require("../src/lib/hostMemoryFormatting");
|
|
43
54
|
(0, runtimeEnv_1.loadRuntimeEnv)();
|
|
55
|
+
let runtimeHostOverride = null;
|
|
56
|
+
let shutdownPromise = null;
|
|
57
|
+
let processIranti = null;
|
|
58
|
+
const HOST_SETUP_CHECKS = {
|
|
59
|
+
claude_code: { file: 'CLAUDE.md', command: 'iranti claude-setup .' },
|
|
60
|
+
codex: { file: 'AGENTS.md', command: 'iranti codex-setup' },
|
|
61
|
+
codex_cli: { file: 'AGENTS.md', command: 'iranti codex-setup' },
|
|
62
|
+
codex_vscode: { file: 'AGENTS.md', command: 'iranti codex-setup' },
|
|
63
|
+
};
|
|
64
|
+
function checkHostSetup(host, cwd) {
|
|
65
|
+
if (!host)
|
|
66
|
+
return [];
|
|
67
|
+
const check = HOST_SETUP_CHECKS[host];
|
|
68
|
+
if (!check)
|
|
69
|
+
return [];
|
|
70
|
+
const filePath = node_path_1.default.join(cwd, check.file);
|
|
71
|
+
if (!node_fs_1.default.existsSync(filePath)) {
|
|
72
|
+
return [`⚠ ${check.file} not found in ${cwd}. Run \`${check.command}\` to complete host setup for this project. Until then, operating rules will not be enforced across sessions.`];
|
|
73
|
+
}
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
44
76
|
function printHelp() {
|
|
45
77
|
console.log([
|
|
46
78
|
'Iranti MCP Server',
|
|
@@ -59,7 +91,8 @@ function printHelp() {
|
|
|
59
91
|
' IRANTI_MCP_AGENT_DESCRIPTION Default agent description',
|
|
60
92
|
' IRANTI_MCP_AGENT_MODEL Default agent model label',
|
|
61
93
|
' IRANTI_MCP_DEFAULT_SOURCE Default write source (default: ClaudeCode)',
|
|
62
|
-
'
|
|
94
|
+
' IRANTI_MCP_HOST Host label for session ledger events (default: generic_mcp)',
|
|
95
|
+
' IRANTI_AUTO_REMEMBER Opt-in prompt-side auto-save before pre-response attend()',
|
|
63
96
|
'',
|
|
64
97
|
'This server is intended for Claude Code and other MCP clients over stdio.',
|
|
65
98
|
'If you run `iranti mcp` directly in a terminal, it will stay running and wait for an MCP client.',
|
|
@@ -72,13 +105,39 @@ function requireConnectionString() {
|
|
|
72
105
|
}
|
|
73
106
|
return connectionString;
|
|
74
107
|
}
|
|
75
|
-
function
|
|
76
|
-
return
|
|
77
|
-
||
|
|
108
|
+
function currentHost(host) {
|
|
109
|
+
return host?.trim()
|
|
110
|
+
|| runtimeHostOverride?.trim()
|
|
111
|
+
|| process.env.IRANTI_MCP_HOST?.trim();
|
|
112
|
+
}
|
|
113
|
+
function defaultAgentId(host) {
|
|
114
|
+
const resolvedHost = currentHost(host);
|
|
115
|
+
if (resolvedHost === 'claude_code') {
|
|
116
|
+
return process.env.IRANTI_CLAUDE_AGENT_ID?.trim()
|
|
117
|
+
|| process.env.IRANTI_AGENT_ID?.trim()
|
|
118
|
+
|| 'claude_code';
|
|
119
|
+
}
|
|
120
|
+
const explicit = process.env.IRANTI_MCP_DEFAULT_AGENT?.trim();
|
|
121
|
+
if (explicit)
|
|
122
|
+
return explicit;
|
|
123
|
+
if (resolvedHost === 'codex' || resolvedHost === 'codex_cli' || resolvedHost === 'codex_vscode') {
|
|
124
|
+
return 'codex_code';
|
|
125
|
+
}
|
|
126
|
+
return process.env.IRANTI_AGENT_ID?.trim()
|
|
78
127
|
|| 'claude_code';
|
|
79
128
|
}
|
|
80
|
-
function defaultWriteSource() {
|
|
81
|
-
|
|
129
|
+
function defaultWriteSource(host) {
|
|
130
|
+
const resolvedHost = currentHost(host);
|
|
131
|
+
if (resolvedHost === 'claude_code') {
|
|
132
|
+
return 'ClaudeCode';
|
|
133
|
+
}
|
|
134
|
+
const explicit = process.env.IRANTI_MCP_DEFAULT_SOURCE?.trim();
|
|
135
|
+
if (explicit)
|
|
136
|
+
return explicit;
|
|
137
|
+
if (resolvedHost === 'codex' || resolvedHost === 'codex_cli' || resolvedHost === 'codex_vscode') {
|
|
138
|
+
return 'Codex';
|
|
139
|
+
}
|
|
140
|
+
return 'MCP';
|
|
82
141
|
}
|
|
83
142
|
function safeJsonParse(raw) {
|
|
84
143
|
try {
|
|
@@ -115,18 +174,57 @@ function parseIsoDate(raw) {
|
|
|
115
174
|
}
|
|
116
175
|
return parsed;
|
|
117
176
|
}
|
|
118
|
-
async function ensureDefaultAgent(iranti) {
|
|
119
|
-
const agentId = defaultAgentId();
|
|
177
|
+
async function ensureDefaultAgent(iranti, host) {
|
|
178
|
+
const agentId = defaultAgentId(host);
|
|
179
|
+
const resolvedHost = currentHost(host);
|
|
180
|
+
const isCodexHost = resolvedHost === 'codex' || resolvedHost === 'codex_cli' || resolvedHost === 'codex_vscode';
|
|
120
181
|
await iranti.registerAgent({
|
|
121
182
|
agentId,
|
|
122
|
-
name: process.env.IRANTI_MCP_AGENT_NAME?.trim() || 'Claude Code',
|
|
123
|
-
description: process.env.IRANTI_MCP_AGENT_DESCRIPTION?.trim() || 'Claude Code MCP client',
|
|
183
|
+
name: process.env.IRANTI_MCP_AGENT_NAME?.trim() || (isCodexHost ? 'Codex' : 'Claude Code'),
|
|
184
|
+
description: process.env.IRANTI_MCP_AGENT_DESCRIPTION?.trim() || (isCodexHost ? 'Codex MCP client' : 'Claude Code MCP client'),
|
|
124
185
|
capabilities: ['memory_read', 'memory_write', 'hybrid_search', 'working_memory'],
|
|
125
|
-
model: process.env.IRANTI_MCP_AGENT_MODEL?.trim()
|
|
186
|
+
model: process.env.IRANTI_MCP_AGENT_MODEL?.trim()
|
|
187
|
+
|| process.env.ANTHROPIC_MODEL
|
|
188
|
+
|| (isCodexHost ? 'codex' : 'claude-code'),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
function syncRuntimeLedgerContext(iranti, host, agent) {
|
|
192
|
+
iranti.setSessionLedgerContext({
|
|
193
|
+
source: 'mcp',
|
|
194
|
+
host: currentHost(host) ?? null,
|
|
195
|
+
agentId: agent?.trim() || defaultAgentId(host),
|
|
126
196
|
});
|
|
127
197
|
}
|
|
128
|
-
function withDefaultAgent(agent) {
|
|
129
|
-
return agent?.trim() || defaultAgentId();
|
|
198
|
+
function withDefaultAgent(agent, host) {
|
|
199
|
+
return agent?.trim() || defaultAgentId(host);
|
|
200
|
+
}
|
|
201
|
+
function resolveToolAgent(agent, agentId, host) {
|
|
202
|
+
return withDefaultAgent(agentId ?? agent, host);
|
|
203
|
+
}
|
|
204
|
+
function resolveToolHost(host) {
|
|
205
|
+
const trimmed = host?.trim();
|
|
206
|
+
if (trimmed) {
|
|
207
|
+
runtimeHostOverride = trimmed;
|
|
208
|
+
return trimmed;
|
|
209
|
+
}
|
|
210
|
+
return currentHost();
|
|
211
|
+
}
|
|
212
|
+
function protocolViolationResult(error) {
|
|
213
|
+
return {
|
|
214
|
+
content: [
|
|
215
|
+
{
|
|
216
|
+
type: 'text',
|
|
217
|
+
text: `${error.protocolViolation.message}\n\n${JSON.stringify({
|
|
218
|
+
blocked: true,
|
|
219
|
+
protocolViolation: error.protocolViolation,
|
|
220
|
+
}, null, 2)}`,
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
structuredContent: {
|
|
224
|
+
blocked: true,
|
|
225
|
+
protocolViolation: error.protocolViolation,
|
|
226
|
+
},
|
|
227
|
+
};
|
|
130
228
|
}
|
|
131
229
|
function normalizeRecentMessages(messages) {
|
|
132
230
|
if (!Array.isArray(messages))
|
|
@@ -139,6 +237,95 @@ function normalizeRecentMessages(messages) {
|
|
|
139
237
|
function isInteractiveTerminalLaunch() {
|
|
140
238
|
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
141
239
|
}
|
|
240
|
+
function clearProcessAttendants() {
|
|
241
|
+
for (const agentId of (0, attendant_1.activeAttendants)()) {
|
|
242
|
+
(0, attendant_1.clearAttendant)(agentId);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async function checkpointActiveAttendants(iranti, reason) {
|
|
246
|
+
const agents = (0, attendant_1.activeAttendants)();
|
|
247
|
+
for (const agentId of agents) {
|
|
248
|
+
const brief = (0, attendant_1.getAttendant)(agentId).getBrief();
|
|
249
|
+
const existing = brief?.sessionCheckpoint;
|
|
250
|
+
if (!existing?.task)
|
|
251
|
+
continue;
|
|
252
|
+
const existingCheckpoint = existing.checkpoint ?? {};
|
|
253
|
+
await iranti.checkpoint({
|
|
254
|
+
agentId,
|
|
255
|
+
task: existing.task,
|
|
256
|
+
recentMessages: [],
|
|
257
|
+
checkpoint: {
|
|
258
|
+
currentStep: existingCheckpoint.currentStep ?? 'in progress',
|
|
259
|
+
nextStep: existingCheckpoint.nextStep,
|
|
260
|
+
openRisks: existingCheckpoint.openRisks,
|
|
261
|
+
recentOutputs: existingCheckpoint.recentOutputs,
|
|
262
|
+
actions: existingCheckpoint.actions,
|
|
263
|
+
fileChanges: existingCheckpoint.fileChanges,
|
|
264
|
+
entityTargets: existingCheckpoint.entityTargets,
|
|
265
|
+
notes: `Host exited gracefully (${reason}). Resume from next step if applicable.`,
|
|
266
|
+
},
|
|
267
|
+
sessionId: existing.sessionId,
|
|
268
|
+
}).catch(() => undefined);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function resolveAttendLatestMessage(input) {
|
|
272
|
+
const latestMessage = input.latestMessage?.trim();
|
|
273
|
+
if (latestMessage)
|
|
274
|
+
return latestMessage;
|
|
275
|
+
const message = input.message?.trim();
|
|
276
|
+
if (message)
|
|
277
|
+
return message;
|
|
278
|
+
throw new Error('iranti_attend requires latestMessage or message.');
|
|
279
|
+
}
|
|
280
|
+
async function shutdownProcess(code, reason) {
|
|
281
|
+
if (shutdownPromise) {
|
|
282
|
+
return shutdownPromise;
|
|
283
|
+
}
|
|
284
|
+
shutdownPromise = (async () => {
|
|
285
|
+
try {
|
|
286
|
+
if (processIranti) {
|
|
287
|
+
await checkpointActiveAttendants(processIranti, reason).catch(() => undefined);
|
|
288
|
+
}
|
|
289
|
+
clearProcessAttendants();
|
|
290
|
+
await (0, staffEventRegistry_1.flushStaffEventEmitter)().catch(() => undefined);
|
|
291
|
+
await (0, client_1.disconnectDb)().catch(() => undefined);
|
|
292
|
+
}
|
|
293
|
+
finally {
|
|
294
|
+
if (isInteractiveTerminalLaunch()) {
|
|
295
|
+
console.error(`[iranti-mcp] shutting down (${reason}).`);
|
|
296
|
+
}
|
|
297
|
+
process.exitCode = code;
|
|
298
|
+
}
|
|
299
|
+
})();
|
|
300
|
+
return shutdownPromise;
|
|
301
|
+
}
|
|
302
|
+
function installShutdownHooks(transport) {
|
|
303
|
+
let shutdownRequested = false;
|
|
304
|
+
const requestShutdown = (code, reason) => {
|
|
305
|
+
if (shutdownRequested)
|
|
306
|
+
return;
|
|
307
|
+
shutdownRequested = true;
|
|
308
|
+
void shutdownProcess(code, reason);
|
|
309
|
+
};
|
|
310
|
+
transport.onclose = () => {
|
|
311
|
+
requestShutdown(0, 'transport_closed');
|
|
312
|
+
};
|
|
313
|
+
// When stdin is already ending or closing, asking the SDK transport to close
|
|
314
|
+
// can re-touch the same handle. On Windows that has been reproducing a
|
|
315
|
+
// UV_HANDLE_CLOSING assertion during shutdown, so we exit directly here.
|
|
316
|
+
process.stdin.once('end', () => requestShutdown(0, 'stdin_end'));
|
|
317
|
+
process.stdin.once('close', () => requestShutdown(0, 'stdin_close'));
|
|
318
|
+
process.stdin.once('error', () => requestShutdown(1, 'stdin_error'));
|
|
319
|
+
process.stdout.once('error', (error) => {
|
|
320
|
+
requestShutdown(error?.code === 'EPIPE' ? 0 : 1, error?.code === 'EPIPE' ? 'stdout_epipe' : 'stdout_error');
|
|
321
|
+
});
|
|
322
|
+
process.once('SIGINT', () => {
|
|
323
|
+
requestShutdown(130, 'sigint');
|
|
324
|
+
});
|
|
325
|
+
process.once('SIGTERM', () => {
|
|
326
|
+
requestShutdown(143, 'sigterm');
|
|
327
|
+
});
|
|
328
|
+
}
|
|
142
329
|
async function main() {
|
|
143
330
|
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
144
331
|
printHelp();
|
|
@@ -147,11 +334,16 @@ async function main() {
|
|
|
147
334
|
const iranti = (0, createFirstPartyIranti_1.createFirstPartyIranti)({
|
|
148
335
|
connectionString: requireConnectionString(),
|
|
149
336
|
llmProvider: process.env.LLM_PROVIDER,
|
|
337
|
+
sessionLedgerSource: 'mcp',
|
|
338
|
+
sessionLedgerHost: currentHost() || 'generic_mcp',
|
|
339
|
+
sessionLedgerAgentId: defaultAgentId(),
|
|
340
|
+
dbPoolMax: 3,
|
|
150
341
|
});
|
|
342
|
+
processIranti = iranti;
|
|
151
343
|
await ensureDefaultAgent(iranti);
|
|
152
344
|
const server = new mcp_js_1.McpServer({
|
|
153
345
|
name: 'iranti-mcp',
|
|
154
|
-
version: '0.2
|
|
346
|
+
version: '0.3.2',
|
|
155
347
|
});
|
|
156
348
|
server.registerTool('iranti_handshake', {
|
|
157
349
|
description: `Initialize or refresh an agent's working-memory brief for the current task.
|
|
@@ -166,13 +358,23 @@ Do not use this as a per-turn retrieval tool; use iranti_attend.`,
|
|
|
166
358
|
task: z.string().min(1).describe('The current task or objective.'),
|
|
167
359
|
recentMessages: z.array(z.string()).optional().describe('Recent conversation messages.'),
|
|
168
360
|
agent: z.string().optional().describe('Override the default agent id.'),
|
|
361
|
+
agentId: z.string().optional().describe('Alias for agent. Override the default agent id.'),
|
|
362
|
+
host: z.string().optional().describe('Host identifier (e.g. claude_code, codex). Used to verify host setup has been run for this project.'),
|
|
169
363
|
},
|
|
170
|
-
}, async ({ task, recentMessages, agent }) => {
|
|
364
|
+
}, async ({ task, recentMessages, agent, agentId, host }) => {
|
|
365
|
+
const resolvedHost = resolveToolHost(host);
|
|
366
|
+
const resolvedAgent = resolveToolAgent(agent, agentId, resolvedHost);
|
|
367
|
+
syncRuntimeLedgerContext(iranti, resolvedHost, resolvedAgent);
|
|
368
|
+
await ensureDefaultAgent(iranti, resolvedHost);
|
|
171
369
|
const result = await iranti.handshake({
|
|
172
|
-
agent:
|
|
370
|
+
agent: resolvedAgent,
|
|
173
371
|
task,
|
|
174
372
|
recentMessages: normalizeRecentMessages(recentMessages),
|
|
175
373
|
});
|
|
374
|
+
const setupWarnings = checkHostSetup(resolvedHost, process.cwd());
|
|
375
|
+
if (setupWarnings.length > 0) {
|
|
376
|
+
return textResult({ ...result, setupWarnings });
|
|
377
|
+
}
|
|
176
378
|
return textResult(result);
|
|
177
379
|
});
|
|
178
380
|
server.registerTool('iranti_attend', {
|
|
@@ -186,33 +388,140 @@ be added to context if relevant memory is missing. If no handshake has been
|
|
|
186
388
|
performed yet for this agent in the current process, attend will auto-bootstrap
|
|
187
389
|
the session first and report that in the result metadata.
|
|
188
390
|
This is the minimum safe pre-reply call even when the host skipped handshake.
|
|
189
|
-
Omitting currentContext falls back to
|
|
190
|
-
full visible context when available
|
|
391
|
+
Omitting currentContext falls back to the latest message only; pass the
|
|
392
|
+
full visible context when available. For host compatibility, message is
|
|
393
|
+
accepted as an alias for latestMessage. When phase='post-response', pass the
|
|
394
|
+
assistant response so Iranti can persist strict continuity facts and shared
|
|
395
|
+
checkpoint breadcrumbs before closing the turn.`,
|
|
191
396
|
inputSchema: {
|
|
192
|
-
latestMessage: z.string().min(1).describe('The latest user or assistant message.'),
|
|
397
|
+
latestMessage: z.string().min(1).optional().describe('The latest user or assistant message.'),
|
|
398
|
+
message: z.string().min(1).optional().describe('Alias for latestMessage, accepted for host compatibility.'),
|
|
193
399
|
currentContext: z.string().optional().describe('Current visible context window.'),
|
|
194
400
|
entityHints: z.array(z.string()).optional().describe('Optional entity hints in entityType/entityId format.'),
|
|
195
401
|
maxFacts: z.number().int().min(1).max(20).optional().describe('Maximum facts to inject.'),
|
|
196
402
|
forceInject: z.boolean().optional().describe('Force a memory injection decision.'),
|
|
403
|
+
phase: z.enum(['pre-response', 'post-response', 'mid-turn']).optional().describe("Call phase: 'pre-response' before replying, 'post-response' after replying. Enables precise compliance tracking."),
|
|
197
404
|
agent: z.string().optional().describe('Override the default agent id.'),
|
|
405
|
+
agentId: z.string().optional().describe('Alias for agent. Override the default agent id.'),
|
|
198
406
|
},
|
|
199
|
-
}, async ({ latestMessage, currentContext, entityHints, maxFacts, forceInject, agent }) => {
|
|
200
|
-
const resolvedAgent =
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
407
|
+
}, async ({ latestMessage, message, currentContext, entityHints, maxFacts, forceInject, phase, agent, agentId }) => {
|
|
408
|
+
const resolvedAgent = resolveToolAgent(agent, agentId);
|
|
409
|
+
syncRuntimeLedgerContext(iranti, undefined, resolvedAgent);
|
|
410
|
+
const resolvedLatestMessage = resolveAttendLatestMessage({ latestMessage, message });
|
|
411
|
+
try {
|
|
412
|
+
if (phase === 'post-response') {
|
|
413
|
+
await (0, autoRemember_1.rememberAssistantResponseFacts)({
|
|
414
|
+
iranti,
|
|
415
|
+
response: resolvedLatestMessage,
|
|
416
|
+
agent: resolvedAgent,
|
|
417
|
+
source: defaultWriteSource(),
|
|
418
|
+
ledgerContext: {
|
|
419
|
+
source: 'mcp',
|
|
420
|
+
host: currentHost() || 'generic_mcp',
|
|
421
|
+
},
|
|
422
|
+
});
|
|
423
|
+
const checkpoint = (0, assistantCheckpoint_1.extractAssistantCheckpointPayload)(resolvedLatestMessage);
|
|
424
|
+
const projectEntity = process.env.IRANTI_MEMORY_ENTITY?.trim();
|
|
425
|
+
if (checkpoint && projectEntity) {
|
|
426
|
+
await iranti.checkpoint({
|
|
427
|
+
agent: resolvedAgent,
|
|
428
|
+
task: `Post-response checkpoint for ${resolvedAgent}`,
|
|
429
|
+
recentMessages: [],
|
|
430
|
+
checkpoint: {
|
|
431
|
+
...checkpoint,
|
|
432
|
+
entityTargets: [projectEntity],
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
else if ((0, autoRemember_1.isAutoRememberEnabled)()) {
|
|
438
|
+
await (0, autoRemember_1.autoRememberPromptFacts)({
|
|
439
|
+
iranti,
|
|
440
|
+
prompt: resolvedLatestMessage,
|
|
441
|
+
agent: resolvedAgent,
|
|
442
|
+
source: defaultWriteSource(),
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
const result = await iranti.attend({
|
|
205
446
|
agent: resolvedAgent,
|
|
206
|
-
|
|
447
|
+
latestMessage: resolvedLatestMessage,
|
|
448
|
+
currentContext: currentContext ?? resolvedLatestMessage,
|
|
449
|
+
entityHints,
|
|
450
|
+
maxFacts,
|
|
451
|
+
forceInject,
|
|
452
|
+
phase,
|
|
207
453
|
});
|
|
454
|
+
const injectionBlock = result.shouldInject
|
|
455
|
+
? (0, hostMemoryFormatting_1.formatStructuredFactBlock)(result.facts, {
|
|
456
|
+
title: 'Iranti Retrieved Memory',
|
|
457
|
+
includeValues: true,
|
|
458
|
+
})
|
|
459
|
+
: '';
|
|
460
|
+
return textResult({
|
|
461
|
+
...result,
|
|
462
|
+
injectionBlock,
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
catch (error) {
|
|
466
|
+
if (error instanceof sdk_1.ProtocolViolationError) {
|
|
467
|
+
return protocolViolationResult(error);
|
|
468
|
+
}
|
|
469
|
+
throw error;
|
|
208
470
|
}
|
|
209
|
-
|
|
471
|
+
});
|
|
472
|
+
server.registerTool('iranti_checkpoint', {
|
|
473
|
+
description: `Persist a shared progress checkpoint while you work.
|
|
474
|
+
Use this at meaningful milestones so current step, next step, open risks,
|
|
475
|
+
recent outputs, structured actions, and shared entity breadcrumbs survive across turns,
|
|
476
|
+
sessions, and agents. This is the strongest shared-RAM tool for active work:
|
|
477
|
+
prefer it over ad-hoc prose when you need another session or another agent
|
|
478
|
+
to pick up where you left off. If entityTargets are supplied, Iranti also
|
|
479
|
+
writes canonical shared breadcrumbs such as current_step, next_step,
|
|
480
|
+
open_risks, recent_actions, and recent_file_changes to those entities for handoff.`,
|
|
481
|
+
inputSchema: {
|
|
482
|
+
task: z.string().min(1).describe('Current task or objective for the active checkpoint.'),
|
|
483
|
+
recentMessages: z.array(z.string()).optional().describe('Recent messages that help fingerprint the active task.'),
|
|
484
|
+
currentStep: z.string().optional().describe('What is being worked on right now.'),
|
|
485
|
+
nextStep: z.string().optional().describe('The next step another session or agent should take.'),
|
|
486
|
+
openRisks: z.array(z.string()).optional().describe('Open risks or blockers that still matter.'),
|
|
487
|
+
recentOutputs: z.array(z.string()).optional().describe('Important outputs or artifacts produced so far.'),
|
|
488
|
+
actions: z.array(z.object({
|
|
489
|
+
kind: z.string().min(1).describe('Action kind such as command, test, edit, search, validation, or decision.'),
|
|
490
|
+
summary: z.string().min(1).describe('Compact description of what action happened.'),
|
|
491
|
+
status: z.string().optional().describe('Optional outcome such as passed, failed, succeeded, or skipped.'),
|
|
492
|
+
target: z.string().optional().describe('Optional command, file, test, or subsystem the action touched.'),
|
|
493
|
+
detail: z.string().optional().describe('Optional compact supporting detail for later recovery.'),
|
|
494
|
+
})).optional().describe('Structured actions completed so far, such as commands, tests, searches, or validations.'),
|
|
495
|
+
fileChanges: z.array(z.object({
|
|
496
|
+
action: z.string().min(1).describe('Change action such as created, updated, moved, renamed, or deleted.'),
|
|
497
|
+
path: z.string().min(1).describe('Primary file path affected by the action.'),
|
|
498
|
+
toPath: z.string().optional().describe('Destination path for move/rename actions.'),
|
|
499
|
+
purpose: z.string().optional().describe('Why the file changed or what role it now serves.'),
|
|
500
|
+
})).optional().describe('Structured file actions produced so far.'),
|
|
501
|
+
entityTargets: z.array(z.string()).optional().describe('Shared entities that should receive checkpoint breadcrumbs, in entityType/entityId format.'),
|
|
502
|
+
notes: z.string().optional().describe('Compact extra checkpoint notes that aid handoff.'),
|
|
503
|
+
sessionId: z.string().optional().describe('Optional existing session id to refresh.'),
|
|
504
|
+
agent: z.string().optional().describe('Override the default agent id.'),
|
|
505
|
+
agentId: z.string().optional().describe('Alias for agent. Override the default agent id.'),
|
|
506
|
+
},
|
|
507
|
+
}, async ({ task, recentMessages, currentStep, nextStep, openRisks, recentOutputs, actions, fileChanges, entityTargets, notes, sessionId, agent, agentId }) => {
|
|
508
|
+
const resolvedAgent = resolveToolAgent(agent, agentId);
|
|
509
|
+
syncRuntimeLedgerContext(iranti, undefined, resolvedAgent);
|
|
510
|
+
const result = await iranti.checkpoint({
|
|
210
511
|
agent: resolvedAgent,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
512
|
+
task,
|
|
513
|
+
recentMessages: normalizeRecentMessages(recentMessages),
|
|
514
|
+
sessionId,
|
|
515
|
+
checkpoint: {
|
|
516
|
+
currentStep,
|
|
517
|
+
nextStep,
|
|
518
|
+
openRisks,
|
|
519
|
+
recentOutputs,
|
|
520
|
+
actions,
|
|
521
|
+
fileChanges,
|
|
522
|
+
entityTargets,
|
|
523
|
+
notes,
|
|
524
|
+
},
|
|
216
525
|
});
|
|
217
526
|
return textResult(result);
|
|
218
527
|
});
|
|
@@ -223,18 +532,31 @@ full visible context when available.`,
|
|
|
223
532
|
entityHints: z.array(z.string()).optional().describe('Optional entity hints in entityType/entityId format.'),
|
|
224
533
|
maxFacts: z.number().int().min(1).max(20).optional().describe('Maximum facts to recover.'),
|
|
225
534
|
agent: z.string().optional().describe('Override the default agent id.'),
|
|
535
|
+
agentId: z.string().optional().describe('Alias for agent. Override the default agent id.'),
|
|
226
536
|
},
|
|
227
|
-
}, async ({ currentContext, entityHints, maxFacts, agent }) => {
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
537
|
+
}, async ({ currentContext, entityHints, maxFacts, agent, agentId }) => {
|
|
538
|
+
const resolvedAgent = resolveToolAgent(agent, agentId);
|
|
539
|
+
syncRuntimeLedgerContext(iranti, undefined, resolvedAgent);
|
|
540
|
+
try {
|
|
541
|
+
const result = await iranti.observe({
|
|
542
|
+
agent: resolvedAgent,
|
|
543
|
+
currentContext,
|
|
544
|
+
entityHints,
|
|
545
|
+
maxFacts,
|
|
546
|
+
});
|
|
547
|
+
return textResult(result);
|
|
548
|
+
}
|
|
549
|
+
catch (error) {
|
|
550
|
+
if (error instanceof sdk_1.ProtocolViolationError) {
|
|
551
|
+
return protocolViolationResult(error);
|
|
552
|
+
}
|
|
553
|
+
throw error;
|
|
554
|
+
}
|
|
235
555
|
});
|
|
236
556
|
server.registerTool('iranti_query', {
|
|
237
557
|
description: `Retrieve the current fact for an exact entity+key lookup.
|
|
558
|
+
REQUIRED: call iranti_attend before this discovery tool so Iranti can decide
|
|
559
|
+
whether memory should be injected before exact lookup.
|
|
238
560
|
Use this when you already know both the entity and the key.
|
|
239
561
|
Returns the current value, summary, confidence, source, and
|
|
240
562
|
temporal metadata when available. Prefer this over iranti_search
|
|
@@ -243,15 +565,62 @@ memory alone before checking Iranti.`,
|
|
|
243
565
|
inputSchema: {
|
|
244
566
|
entity: z.string().min(1).describe('Entity in entityType/entityId format.'),
|
|
245
567
|
key: z.string().min(1).describe('Fact key to retrieve.'),
|
|
568
|
+
agent: z.string().optional().describe('Override the default agent id for protocol tracking.'),
|
|
569
|
+
agentId: z.string().optional().describe('Alias for agent. Override the default agent id for protocol tracking.'),
|
|
246
570
|
},
|
|
247
|
-
}, async ({ entity, key }) => {
|
|
248
|
-
const
|
|
249
|
-
|
|
571
|
+
}, async ({ entity, key, agent, agentId }) => {
|
|
572
|
+
const resolvedAgent = resolveToolAgent(agent, agentId);
|
|
573
|
+
syncRuntimeLedgerContext(iranti, undefined, resolvedAgent);
|
|
574
|
+
try {
|
|
575
|
+
const result = await iranti.query(entity, key);
|
|
576
|
+
return textResult(result);
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
if (error instanceof sdk_1.ProtocolViolationError) {
|
|
580
|
+
return protocolViolationResult(error);
|
|
581
|
+
}
|
|
582
|
+
throw error;
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
server.registerTool('iranti_history', {
|
|
586
|
+
description: `Retrieve the full version history of a fact for an exact entity+key pair.
|
|
587
|
+
Returns all archived past values plus the current value, ordered oldest-first.
|
|
588
|
+
Each entry includes value, summary, confidence, source, validFrom, validUntil,
|
|
589
|
+
isCurrent, archivedReason, and resolutionState.
|
|
590
|
+
REQUIRED: call iranti_attend before this discovery tool so Iranti can decide
|
|
591
|
+
whether memory should be injected first.
|
|
592
|
+
Use this to understand how a fact evolved over time — decisions that changed,
|
|
593
|
+
blockers that were resolved, values that were contested or superseded.`,
|
|
594
|
+
inputSchema: {
|
|
595
|
+
entity: z.string().min(1).describe('Entity in entityType/entityId format.'),
|
|
596
|
+
key: z.string().min(1).describe('Fact key to retrieve history for.'),
|
|
597
|
+
limit: z.number().int().min(1).optional().describe('Maximum number of entries to return (applied after sorting oldest-first).'),
|
|
598
|
+
includeExpired: z.boolean().optional().describe('Include entries that expired without being superseded.'),
|
|
599
|
+
includeContested: z.boolean().optional().describe('Include entries that were contested or escalated.'),
|
|
600
|
+
agent: z.string().optional().describe('Override the default agent id for protocol tracking.'),
|
|
601
|
+
agentId: z.string().optional().describe('Alias for agent. Override the default agent id for protocol tracking.'),
|
|
602
|
+
},
|
|
603
|
+
}, async ({ entity, key, limit, includeExpired, includeContested, agent, agentId }) => {
|
|
604
|
+
const resolvedAgent = resolveToolAgent(agent, agentId);
|
|
605
|
+
syncRuntimeLedgerContext(iranti, undefined, resolvedAgent);
|
|
606
|
+
try {
|
|
607
|
+
const history = await iranti.history(entity, key, { includeExpired, includeContested });
|
|
608
|
+
const result = limit != null ? history.slice(0, limit) : history;
|
|
609
|
+
return textResult(result);
|
|
610
|
+
}
|
|
611
|
+
catch (error) {
|
|
612
|
+
if (error instanceof sdk_1.ProtocolViolationError) {
|
|
613
|
+
return protocolViolationResult(error);
|
|
614
|
+
}
|
|
615
|
+
throw error;
|
|
616
|
+
}
|
|
250
617
|
});
|
|
251
618
|
server.registerTool('iranti_search', {
|
|
252
619
|
description: `Search shared memory with natural language when the exact entity
|
|
253
620
|
or key is unknown. Uses hybrid lexical and vector search across
|
|
254
621
|
stored facts. Use this for discovery and recall, not exact lookup.
|
|
622
|
+
REQUIRED: call iranti_attend before this discovery tool so Iranti can decide
|
|
623
|
+
whether memory should be injected before search.
|
|
255
624
|
If the user asks what they previously told you and you do not know
|
|
256
625
|
the exact key, use this before saying you do not know.`,
|
|
257
626
|
inputSchema: {
|
|
@@ -262,18 +631,30 @@ the exact key, use this before saying you do not know.`,
|
|
|
262
631
|
lexicalWeight: z.number().min(0).max(1).optional().describe('Lexical ranking weight.'),
|
|
263
632
|
vectorWeight: z.number().min(0).max(1).optional().describe('Vector similarity weight.'),
|
|
264
633
|
minScore: z.number().min(0).max(1).optional().describe('Minimum final score threshold.'),
|
|
634
|
+
agent: z.string().optional().describe('Override the default agent id for protocol tracking.'),
|
|
635
|
+
agentId: z.string().optional().describe('Alias for agent. Override the default agent id for protocol tracking.'),
|
|
265
636
|
},
|
|
266
|
-
}, async ({ query, entityType, entityId, limit, lexicalWeight, vectorWeight, minScore }) => {
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
637
|
+
}, async ({ query, entityType, entityId, limit, lexicalWeight, vectorWeight, minScore, agent, agentId }) => {
|
|
638
|
+
const resolvedAgent = resolveToolAgent(agent, agentId);
|
|
639
|
+
syncRuntimeLedgerContext(iranti, undefined, resolvedAgent);
|
|
640
|
+
try {
|
|
641
|
+
const result = await iranti.search({
|
|
642
|
+
query,
|
|
643
|
+
entityType,
|
|
644
|
+
entityId,
|
|
645
|
+
limit,
|
|
646
|
+
lexicalWeight,
|
|
647
|
+
vectorWeight,
|
|
648
|
+
minScore,
|
|
649
|
+
});
|
|
650
|
+
return textResult(result);
|
|
651
|
+
}
|
|
652
|
+
catch (error) {
|
|
653
|
+
if (error instanceof sdk_1.ProtocolViolationError) {
|
|
654
|
+
return protocolViolationResult(error);
|
|
655
|
+
}
|
|
656
|
+
throw error;
|
|
657
|
+
}
|
|
277
658
|
});
|
|
278
659
|
server.registerTool('iranti_write', {
|
|
279
660
|
description: `Write one durable fact to shared memory for a specific entity.
|
|
@@ -282,7 +663,9 @@ agents, or sessions should retain. Requires: entity ("type/id"),
|
|
|
282
663
|
key, value JSON, and summary. Confidence is optional and defaults
|
|
283
664
|
to 85. Conflicts on the same entity+key are detected automatically
|
|
284
665
|
and may be resolved or escalated. Personal-memory keys honor the
|
|
285
|
-
configured canonical personal entity for this project/session
|
|
666
|
+
configured canonical personal entity for this project/session.
|
|
667
|
+
Use properties JSON when you need structured issue or workflow metadata
|
|
668
|
+
such as issueStatus=open|resolved, severity, or resolution notes.`,
|
|
286
669
|
inputSchema: {
|
|
287
670
|
entity: z.string().min(1).describe('Entity in entityType/entityId format.'),
|
|
288
671
|
key: z.string().min(1).describe('Fact key.'),
|
|
@@ -290,12 +673,16 @@ configured canonical personal entity for this project/session.`,
|
|
|
290
673
|
summary: z.string().min(1).describe('Short retrieval-safe summary.'),
|
|
291
674
|
confidence: z.number().int().min(0).max(100).optional().describe('Raw confidence score.'),
|
|
292
675
|
source: z.string().optional().describe('Source label for provenance.'),
|
|
676
|
+
propertiesJson: z.string().optional().describe('Optional JSON-serialized fact properties for metadata such as issueStatus or severity.'),
|
|
293
677
|
validFrom: z.string().optional().describe('Optional ISO timestamp for when the fact became true/current.'),
|
|
294
678
|
requestId: z.string().optional().describe('Optional idempotency key.'),
|
|
295
679
|
agent: z.string().optional().describe('Override the default agent id.'),
|
|
680
|
+
agentId: z.string().optional().describe('Alias for agent. Override the default agent id.'),
|
|
296
681
|
},
|
|
297
|
-
}, async ({ entity, key, valueJson, summary, confidence, source, validFrom, requestId, agent }) => {
|
|
682
|
+
}, async ({ entity, key, valueJson, summary, confidence, source, propertiesJson, validFrom, requestId, agent, agentId }) => {
|
|
298
683
|
const target = (0, autoRemember_1.resolvePersonalWriteTarget)({ entity, key });
|
|
684
|
+
const resolvedAgent = resolveToolAgent(agent, agentId);
|
|
685
|
+
syncRuntimeLedgerContext(iranti, undefined, resolvedAgent);
|
|
299
686
|
const result = await iranti.write({
|
|
300
687
|
entity: target.entity,
|
|
301
688
|
key,
|
|
@@ -303,7 +690,8 @@ configured canonical personal entity for this project/session.`,
|
|
|
303
690
|
summary,
|
|
304
691
|
confidence: confidence ?? 85,
|
|
305
692
|
source: source?.trim() || defaultWriteSource(),
|
|
306
|
-
agent:
|
|
693
|
+
agent: resolvedAgent,
|
|
694
|
+
properties: propertiesJson ? safeJsonParse(propertiesJson) : undefined,
|
|
307
695
|
validFrom: parseIsoDate(validFrom),
|
|
308
696
|
requestId,
|
|
309
697
|
});
|
|
@@ -313,6 +701,54 @@ configured canonical personal entity for this project/session.`,
|
|
|
313
701
|
...(target.rerouted ? { originalEntity: target.originalEntity, canonicalizedPersonalEntity: true } : {}),
|
|
314
702
|
});
|
|
315
703
|
});
|
|
704
|
+
server.registerTool('iranti_write_issue', {
|
|
705
|
+
description: `Write a canonical open or resolved issue fact on a stable key.
|
|
706
|
+
Use this when you want defects, bugs, or chores to remain first-class shared
|
|
707
|
+
memory instead of loose prose. The same issueId always maps to the same
|
|
708
|
+
issue_<id> key, so changing status from open to resolved archives the prior
|
|
709
|
+
state automatically while preserving history. Prefer this over hand-rolling
|
|
710
|
+
issueStatus properties through iranti_write when the fact is specifically a
|
|
711
|
+
trackable issue lifecycle entry.`,
|
|
712
|
+
inputSchema: {
|
|
713
|
+
entity: z.string().min(1).describe('Owner entity in entityType/entityId format, usually a project entity.'),
|
|
714
|
+
issueId: z.string().min(1).describe('Stable issue identifier that becomes issue_<normalized_id>.'),
|
|
715
|
+
title: z.string().min(1).describe('Short human-readable issue title.'),
|
|
716
|
+
status: z.enum(['open', 'resolved']).describe('Issue lifecycle status.'),
|
|
717
|
+
summary: z.string().min(1).describe('Short retrieval-safe summary of the issue state.'),
|
|
718
|
+
confidence: z.number().int().min(0).max(100).optional().describe('Raw confidence score.'),
|
|
719
|
+
source: z.string().optional().describe('Source label for provenance.'),
|
|
720
|
+
severity: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Optional issue severity.'),
|
|
721
|
+
detailsJson: z.string().optional().describe('Optional JSON-serialized structured issue details.'),
|
|
722
|
+
discoveredAt: z.string().optional().describe('Optional ISO timestamp for when the issue was first observed.'),
|
|
723
|
+
resolvedAt: z.string().optional().describe('Optional ISO timestamp for when the issue was resolved.'),
|
|
724
|
+
resolution: z.string().optional().describe('Optional resolution note for resolved issues.'),
|
|
725
|
+
tags: z.array(z.string()).optional().describe('Optional issue tags.'),
|
|
726
|
+
requestId: z.string().optional().describe('Optional idempotency key.'),
|
|
727
|
+
agent: z.string().optional().describe('Override the default agent id.'),
|
|
728
|
+
agentId: z.string().optional().describe('Alias for agent. Override the default agent id.'),
|
|
729
|
+
},
|
|
730
|
+
}, async ({ entity, issueId, title, status, summary, confidence, source, severity, detailsJson, discoveredAt, resolvedAt, resolution, tags, requestId, agent, agentId }) => {
|
|
731
|
+
const resolvedAgent = resolveToolAgent(agent, agentId);
|
|
732
|
+
syncRuntimeLedgerContext(iranti, undefined, resolvedAgent);
|
|
733
|
+
const result = await iranti.writeIssue({
|
|
734
|
+
entity,
|
|
735
|
+
issueId,
|
|
736
|
+
title,
|
|
737
|
+
status,
|
|
738
|
+
summary,
|
|
739
|
+
confidence: confidence ?? 85,
|
|
740
|
+
source: source?.trim() || defaultWriteSource(),
|
|
741
|
+
agent: resolvedAgent,
|
|
742
|
+
severity,
|
|
743
|
+
details: detailsJson ? safeJsonParse(detailsJson) : undefined,
|
|
744
|
+
discoveredAt,
|
|
745
|
+
resolvedAt,
|
|
746
|
+
resolution,
|
|
747
|
+
tags,
|
|
748
|
+
requestId,
|
|
749
|
+
});
|
|
750
|
+
return textResult(result);
|
|
751
|
+
});
|
|
316
752
|
server.registerTool('iranti_remember_response', {
|
|
317
753
|
description: `Persist a strict durable summary from your own response.
|
|
318
754
|
Use this after you decide to say something like "the next step is ...",
|
|
@@ -327,16 +763,23 @@ this for arbitrary prose or every turn.`,
|
|
|
327
763
|
source: z.string().optional().describe('Optional provenance label override.'),
|
|
328
764
|
confidence: z.number().int().min(0).max(100).optional().describe('Raw confidence score for remembered summaries.'),
|
|
329
765
|
agent: z.string().optional().describe('Override the default agent id.'),
|
|
766
|
+
agentId: z.string().optional().describe('Alias for agent. Override the default agent id.'),
|
|
330
767
|
},
|
|
331
|
-
}, async ({ response, projectEntity, personalEntity, source, confidence, agent }) => {
|
|
768
|
+
}, async ({ response, projectEntity, personalEntity, source, confidence, agent, agentId }) => {
|
|
769
|
+
const resolvedAgent = resolveToolAgent(agent, agentId);
|
|
770
|
+
syncRuntimeLedgerContext(iranti, undefined, resolvedAgent);
|
|
332
771
|
const result = await (0, autoRemember_1.rememberAssistantResponseFacts)({
|
|
333
772
|
iranti,
|
|
334
773
|
response,
|
|
335
|
-
agent:
|
|
774
|
+
agent: resolvedAgent,
|
|
336
775
|
source: source?.trim() || defaultWriteSource(),
|
|
337
776
|
projectEntity,
|
|
338
777
|
personalEntity,
|
|
339
778
|
confidence: confidence ?? 90,
|
|
779
|
+
ledgerContext: {
|
|
780
|
+
source: 'mcp',
|
|
781
|
+
host: currentHost() || 'generic_mcp',
|
|
782
|
+
},
|
|
340
783
|
});
|
|
341
784
|
return textResult(result);
|
|
342
785
|
});
|
|
@@ -348,14 +791,17 @@ this for arbitrary prose or every turn.`,
|
|
|
348
791
|
confidence: z.number().int().min(0).max(100).optional().describe('Raw confidence score.'),
|
|
349
792
|
source: z.string().optional().describe('Source label for provenance.'),
|
|
350
793
|
agent: z.string().optional().describe('Override the default agent id.'),
|
|
794
|
+
agentId: z.string().optional().describe('Alias for agent. Override the default agent id.'),
|
|
351
795
|
},
|
|
352
|
-
}, async ({ entity, content, confidence, source, agent }) => {
|
|
796
|
+
}, async ({ entity, content, confidence, source, agent, agentId }) => {
|
|
797
|
+
const resolvedAgent = resolveToolAgent(agent, agentId);
|
|
798
|
+
syncRuntimeLedgerContext(iranti, undefined, resolvedAgent);
|
|
353
799
|
const result = await iranti.ingest({
|
|
354
800
|
entity,
|
|
355
801
|
content,
|
|
356
802
|
confidence: confidence ?? 80,
|
|
357
803
|
source: source?.trim() || defaultWriteSource(),
|
|
358
|
-
agent:
|
|
804
|
+
agent: resolvedAgent,
|
|
359
805
|
});
|
|
360
806
|
return textResult(result);
|
|
361
807
|
});
|
|
@@ -370,49 +816,106 @@ this for arbitrary prose or every turn.`,
|
|
|
370
816
|
},
|
|
371
817
|
}, async ({ fromEntity, relationshipType, toEntity, propertiesJson, createdBy }) => {
|
|
372
818
|
const properties = propertiesJson ? safeJsonParse(propertiesJson) : undefined;
|
|
819
|
+
const resolvedAgent = withDefaultAgent(createdBy);
|
|
820
|
+
syncRuntimeLedgerContext(iranti, undefined, resolvedAgent);
|
|
373
821
|
const result = await iranti.relate(fromEntity, relationshipType, toEntity, {
|
|
374
|
-
createdBy:
|
|
822
|
+
createdBy: resolvedAgent,
|
|
375
823
|
properties: (properties ?? {}),
|
|
376
824
|
});
|
|
377
825
|
return textResult({ ok: true, result });
|
|
378
826
|
});
|
|
379
827
|
server.registerTool('iranti_related', {
|
|
380
|
-
description:
|
|
828
|
+
description: `Read directly related entities (1 hop) for a given entity.
|
|
829
|
+
REQUIRED: call iranti_attend before this discovery tool so Iranti can decide
|
|
830
|
+
whether memory should be injected before graph traversal.`,
|
|
381
831
|
inputSchema: {
|
|
382
832
|
entity: z.string().min(1).describe('Entity in entityType/entityId format.'),
|
|
833
|
+
agent: z.string().optional().describe('Override the default agent id for protocol tracking.'),
|
|
834
|
+
agentId: z.string().optional().describe('Alias for agent. Override the default agent id for protocol tracking.'),
|
|
383
835
|
},
|
|
384
|
-
}, async ({ entity }) => {
|
|
385
|
-
const
|
|
386
|
-
|
|
836
|
+
}, async ({ entity, agent, agentId }) => {
|
|
837
|
+
const resolvedAgent = resolveToolAgent(agent, agentId);
|
|
838
|
+
syncRuntimeLedgerContext(iranti, undefined, resolvedAgent);
|
|
839
|
+
try {
|
|
840
|
+
const result = await iranti.getRelated(entity);
|
|
841
|
+
return textResult(result);
|
|
842
|
+
}
|
|
843
|
+
catch (error) {
|
|
844
|
+
if (error instanceof sdk_1.ProtocolViolationError) {
|
|
845
|
+
return protocolViolationResult(error);
|
|
846
|
+
}
|
|
847
|
+
throw error;
|
|
848
|
+
}
|
|
387
849
|
});
|
|
388
850
|
server.registerTool('iranti_related_deep', {
|
|
389
|
-
description:
|
|
851
|
+
description: `Read related entities up to N hops deep for a given entity.
|
|
852
|
+
REQUIRED: call iranti_attend before this discovery tool so Iranti can decide
|
|
853
|
+
whether memory should be injected before graph traversal.`,
|
|
390
854
|
inputSchema: {
|
|
391
855
|
entity: z.string().min(1).describe('Entity in entityType/entityId format.'),
|
|
392
856
|
depth: z.number().int().min(1).max(5).optional().describe('Traversal depth.'),
|
|
857
|
+
agent: z.string().optional().describe('Override the default agent id for protocol tracking.'),
|
|
858
|
+
agentId: z.string().optional().describe('Alias for agent. Override the default agent id for protocol tracking.'),
|
|
393
859
|
},
|
|
394
|
-
}, async ({ entity, depth }) => {
|
|
395
|
-
const
|
|
396
|
-
|
|
860
|
+
}, async ({ entity, depth, agent, agentId }) => {
|
|
861
|
+
const resolvedAgent = resolveToolAgent(agent, agentId);
|
|
862
|
+
syncRuntimeLedgerContext(iranti, undefined, resolvedAgent);
|
|
863
|
+
try {
|
|
864
|
+
const result = await iranti.getRelatedDeep(entity, depth ?? 2);
|
|
865
|
+
return textResult(result);
|
|
866
|
+
}
|
|
867
|
+
catch (error) {
|
|
868
|
+
if (error instanceof sdk_1.ProtocolViolationError) {
|
|
869
|
+
return protocolViolationResult(error);
|
|
870
|
+
}
|
|
871
|
+
throw error;
|
|
872
|
+
}
|
|
397
873
|
});
|
|
398
874
|
server.registerTool('iranti_who_knows', {
|
|
399
|
-
description:
|
|
875
|
+
description: `List which agents have written facts about an entity.
|
|
876
|
+
REQUIRED: call iranti_attend before this discovery tool so Iranti can decide
|
|
877
|
+
whether memory should be injected before provenance discovery.`,
|
|
400
878
|
inputSchema: {
|
|
401
879
|
entity: z.string().min(1).describe('Entity in entityType/entityId format.'),
|
|
880
|
+
agent: z.string().optional().describe('Override the default agent id for protocol tracking.'),
|
|
881
|
+
agentId: z.string().optional().describe('Alias for agent. Override the default agent id for protocol tracking.'),
|
|
402
882
|
},
|
|
403
|
-
}, async ({ entity }) => {
|
|
404
|
-
const
|
|
405
|
-
|
|
883
|
+
}, async ({ entity, agent, agentId }) => {
|
|
884
|
+
const resolvedAgent = resolveToolAgent(agent, agentId);
|
|
885
|
+
syncRuntimeLedgerContext(iranti, undefined, resolvedAgent);
|
|
886
|
+
try {
|
|
887
|
+
const result = await iranti.whoKnows(entity);
|
|
888
|
+
return textResult(result);
|
|
889
|
+
}
|
|
890
|
+
catch (error) {
|
|
891
|
+
if (error instanceof sdk_1.ProtocolViolationError) {
|
|
892
|
+
return protocolViolationResult(error);
|
|
893
|
+
}
|
|
894
|
+
throw error;
|
|
895
|
+
}
|
|
406
896
|
});
|
|
407
897
|
if (isInteractiveTerminalLaunch()) {
|
|
408
898
|
console.error('[iranti-mcp] stdio server running; waiting for an MCP client. Press Ctrl+C to exit.');
|
|
409
899
|
}
|
|
410
900
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
901
|
+
installShutdownHooks(transport);
|
|
411
902
|
await server.connect(transport);
|
|
412
903
|
}
|
|
413
904
|
main().catch((error) => {
|
|
414
905
|
const formatted = (0, commandErrors_1.rewriteCommandError)('iranti mcp', error);
|
|
906
|
+
(0, staffEventRegistry_1.getStaffEventEmitter)().emit({
|
|
907
|
+
staffComponent: 'Attendant',
|
|
908
|
+
actionType: 'host_failure',
|
|
909
|
+
agentId: defaultAgentId(),
|
|
910
|
+
source: 'mcp',
|
|
911
|
+
level: 'audit',
|
|
912
|
+
reason: formatted.message,
|
|
913
|
+
metadata: {
|
|
914
|
+
host: currentHost() || 'generic_mcp',
|
|
915
|
+
operation: 'mcp_startup',
|
|
916
|
+
},
|
|
917
|
+
});
|
|
415
918
|
console.error('[iranti-mcp] fatal:', formatted.message);
|
|
416
|
-
|
|
919
|
+
void shutdownProcess(1, 'fatal_error');
|
|
417
920
|
});
|
|
418
921
|
//# sourceMappingURL=iranti-mcp.js.map
|