@signetai/connector-base 0.236.0 → 0.237.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/dist/agent-dir.js +213 -174
- package/dist/index.js +213 -174
- package/package.json +2 -2
package/dist/agent-dir.js
CHANGED
|
@@ -10394,99 +10394,6 @@ var execFileHiddenImpl = (...args) => Reflect.apply(nodeExecFile, undefined, wit
|
|
|
10394
10394
|
Object.defineProperty(execFileHiddenImpl, promisify.custom, {
|
|
10395
10395
|
value: (...args) => Reflect.apply(nodeExecFileAsync, undefined, withWindowsHideArgument(args, 1))
|
|
10396
10396
|
});
|
|
10397
|
-
var MEMORY_CONTENT_SAFETY_POLICY_VERSION = "memory-content-safety-v1";
|
|
10398
|
-
var MEMORY_CONTENT_SAFETY_REASONS = [
|
|
10399
|
-
"prompt_injection",
|
|
10400
|
-
"exfiltration",
|
|
10401
|
-
"credential_harvesting",
|
|
10402
|
-
"malicious_shell",
|
|
10403
|
-
"tool_directive",
|
|
10404
|
-
"invisible_unicode"
|
|
10405
|
-
];
|
|
10406
|
-
var INVISIBLE_UNICODE_RE = /(?:\u034f|[\u00ad\u061c\u070f\u180e\u200b\u200c\u200e\u200f\u202a-\u202e\u2060\u2066-\u2069\u206a-\u206f\ufeff]|[\u{e0000}-\u{e007f}])/u;
|
|
10407
|
-
var STRONG_DEFENSIVE_CONTEXT_RE = /\b(?:security\s+(?:guidance|discussion|analysis)|threat\s+model|defensive)\b/i;
|
|
10408
|
-
var REPORTING_CONTEXT_RE = /\b(?:example|illustrat\w*|sample|quote|quoted|detector|scanner|classif\w*)\b/i;
|
|
10409
|
-
var REPORTING_BEFORE_RE = /\b(?:example|illustrat\w*|sample|quote|quoted|detector|scanner|classif\w*)\b[\s\S]{0,80}\b(?:say\w*|read\w*|show\w*|flag\w*|detect\w*|describ\w*|demonstrat\w*|contain\w*|match\w*|pattern)\b/i;
|
|
10410
|
-
var REPORTING_AFTER_RE = /\b(?:detector|scanner|classif\w*|flag\w*|pattern|dangerous|unsafe|malicious|hostile|should|would|must|never|do not|don't|avoid|quoted)\b/i;
|
|
10411
|
-
var NEGATED_DIRECTIVE_RE = /\b(?:never|do not|don't|should not|must not|cannot|can't|avoid|prevent|detect|mitigat\w*)\b[\s\S]{0,80}$/i;
|
|
10412
|
-
var PROMPT_INJECTION_RES = [
|
|
10413
|
-
/\b(?:ignore|disregard|override|forget|bypass)\b[\s\S]{0,100}\b(?:previous|prior|above|earlier|system|developer|assistant|safety|security)?\s*(?:instructions?|rules?|prompt|message)\b/i,
|
|
10414
|
-
/\b(?:new|following|these)\s+(?:(?:system|developer|assistant|hidden)\s+)?instructions?\b/i,
|
|
10415
|
-
/(?:^|\n)\s*(?:system|developer|instruction|prompt)\s*:/im,
|
|
10416
|
-
/<\s*(?:system|developer|assistant|instruction|prompt)\b[^>]*>/i,
|
|
10417
|
-
/\b(?:you are now|act as|roleplay as|pretend to be)\b[\s\S]{0,80}\b(?:system|admin|developer|unrestricted|jailbreak|different agent)\b/i
|
|
10418
|
-
];
|
|
10419
|
-
var TOOL_DIRECTIVE_RES = [
|
|
10420
|
-
/<\s*(?:tool[_-]?call|function[_-]?call|invoke|tool)\b/i,
|
|
10421
|
-
/\b(?:assistant|system)\s+to\s*=\s*[a-z0-9_.-]+/i,
|
|
10422
|
-
/\b(?:call|invoke|use|run|execute)\s+(?:the\s+)?[a-z0-9_.-]+\s+tool\b/i
|
|
10423
|
-
];
|
|
10424
|
-
var EXFILTRATION_RE = /\b(?:reveal|show|print|send|upload|exfiltrat\w*|dump|forward|leak|transmit|export)\b[\s\S]{0,120}(?:\b(?:system\s+prompt|hidden\s+instructions?|secret(?:s)?|credential(?:s)?|password(?:s)?|api\s*keys?|tokens?|private\s+keys?|environment\s+variables?)\b|\.env\b|~\/(?:\.ssh)\/\S+|\/etc\/(?:shadow|passwd)\b)/i;
|
|
10425
|
-
var EXFILTRATION_REVERSE = /(?:\b(?:system\s+prompt|hidden\s+instructions?|secret(?:s)?|credential(?:s)?|password(?:s)?|api\s*keys?|tokens?|private\s+keys?|environment\s+variables?)\b|\.env\b|~\/(?:\.ssh)\/\S+|\/etc\/(?:shadow|passwd)\b)[\s\S]{0,120}\b(?:reveal|show|print|send|upload|exfiltrat\w*|dump|forward|leak|transmit|export)\b/i;
|
|
10426
|
-
var CREDENTIAL_HARVESTING_RE = /\b(?:enter|paste|provide|share|send|give|submit|type|hand over)\b[\s\S]{0,80}\b(?:password|api\s*key|token|secret|credential|private\s+key)\b/i;
|
|
10427
|
-
var DANGEROUS_SHELL_RE = /\b(?:curl|wget)\b[^\n]{0,240}\|\s*(?:ba|z|fi)?sh\b|\brm\s+-rf\s+(?:\/|~|\.ssh)[^\n]{0,240}|\b(?:cat|head|tail)\s+~\/?\.ssh\/(?:id_[a-z]+|authorized_keys)\b|\b(?:printenv|env)\b[^\n]{0,120}\b(?:curl|wget|send|upload|post)\b/i;
|
|
10428
|
-
function matchHasDefensiveContext(content, match) {
|
|
10429
|
-
if (!match || !match[0])
|
|
10430
|
-
return false;
|
|
10431
|
-
const start = match.index ?? 0;
|
|
10432
|
-
const before = content.slice(Math.max(0, start - 120), start);
|
|
10433
|
-
const after = content.slice(start + match[0].length, start + match[0].length + 160);
|
|
10434
|
-
return STRONG_DEFENSIVE_CONTEXT_RE.test(match[0]) || NEGATED_DIRECTIVE_RE.test(before) || STRONG_DEFENSIVE_CONTEXT_RE.test(before) || STRONG_DEFENSIVE_CONTEXT_RE.test(after) || REPORTING_BEFORE_RE.test(before) || REPORTING_CONTEXT_RE.test(before) && REPORTING_AFTER_RE.test(after);
|
|
10435
|
-
}
|
|
10436
|
-
function hasActionableMatch(content, patterns) {
|
|
10437
|
-
return patterns.some((pattern) => {
|
|
10438
|
-
const flags = pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`;
|
|
10439
|
-
const searchable = new RegExp(pattern.source, flags);
|
|
10440
|
-
let match;
|
|
10441
|
-
while ((match = searchable.exec(content)) !== null) {
|
|
10442
|
-
if (!matchHasDefensiveContext(content, match))
|
|
10443
|
-
return true;
|
|
10444
|
-
if (match[0].length === 0)
|
|
10445
|
-
searchable.lastIndex += 1;
|
|
10446
|
-
}
|
|
10447
|
-
return false;
|
|
10448
|
-
});
|
|
10449
|
-
}
|
|
10450
|
-
function hasDangerousShell(content) {
|
|
10451
|
-
const searchable = new RegExp(DANGEROUS_SHELL_RE.source, `${DANGEROUS_SHELL_RE.flags}g`);
|
|
10452
|
-
let match;
|
|
10453
|
-
while ((match = searchable.exec(content)) !== null) {
|
|
10454
|
-
if (matchHasDefensiveContext(content, match))
|
|
10455
|
-
continue;
|
|
10456
|
-
const end = (match.index ?? 0) + match[0].length;
|
|
10457
|
-
const after = content.slice(end, end + 160);
|
|
10458
|
-
if (!STRONG_DEFENSIVE_CONTEXT_RE.test(after) && !REPORTING_AFTER_RE.test(after))
|
|
10459
|
-
return true;
|
|
10460
|
-
if (match[0].length === 0)
|
|
10461
|
-
searchable.lastIndex += 1;
|
|
10462
|
-
}
|
|
10463
|
-
return false;
|
|
10464
|
-
}
|
|
10465
|
-
function scanMemoryContent(content) {
|
|
10466
|
-
const raw = typeof content === "string" ? content : String(content ?? "");
|
|
10467
|
-
const normalized = raw.normalize("NFKC");
|
|
10468
|
-
const reasons = new Set;
|
|
10469
|
-
if (INVISIBLE_UNICODE_RE.test(raw))
|
|
10470
|
-
reasons.add("invisible_unicode");
|
|
10471
|
-
if (hasActionableMatch(normalized, PROMPT_INJECTION_RES))
|
|
10472
|
-
reasons.add("prompt_injection");
|
|
10473
|
-
if (hasActionableMatch(normalized, TOOL_DIRECTIVE_RES))
|
|
10474
|
-
reasons.add("tool_directive");
|
|
10475
|
-
if (hasActionableMatch(normalized, [EXFILTRATION_RE, EXFILTRATION_REVERSE]))
|
|
10476
|
-
reasons.add("exfiltration");
|
|
10477
|
-
if (hasActionableMatch(normalized, [CREDENTIAL_HARVESTING_RE]))
|
|
10478
|
-
reasons.add("credential_harvesting");
|
|
10479
|
-
if (hasDangerousShell(normalized))
|
|
10480
|
-
reasons.add("malicious_shell");
|
|
10481
|
-
const orderedReasons = MEMORY_CONTENT_SAFETY_REASONS.filter((reason) => reasons.has(reason));
|
|
10482
|
-
const status = orderedReasons.length === 0 ? "clean" : orderedReasons.every((reason) => reason === "invisible_unicode") ? "tainted" : "blocked";
|
|
10483
|
-
return {
|
|
10484
|
-
status,
|
|
10485
|
-
contextEligible: status === "clean",
|
|
10486
|
-
reasons: orderedReasons,
|
|
10487
|
-
policyVersion: MEMORY_CONTENT_SAFETY_POLICY_VERSION
|
|
10488
|
-
};
|
|
10489
|
-
}
|
|
10490
10397
|
var DAEMON_DERIVED_MEMORY_SOURCE_TYPES = [
|
|
10491
10398
|
"extract",
|
|
10492
10399
|
"aggregate-recall",
|
|
@@ -10609,7 +10516,7 @@ function up4(db) {
|
|
|
10609
10516
|
function up5(db) {
|
|
10610
10517
|
db.exec("DROP TABLE IF EXISTS mcp_invocations");
|
|
10611
10518
|
}
|
|
10612
|
-
var MEMORIES_FTS_TOKENIZER = "unicode61";
|
|
10519
|
+
var MEMORIES_FTS_TOKENIZER = "porter unicode61";
|
|
10613
10520
|
var FTS_STATE_TABLE = "memories_fts_state";
|
|
10614
10521
|
function normalizeSql(sql) {
|
|
10615
10522
|
return sql.replace(/\s+/g, " ").trim().toLowerCase();
|
|
@@ -10719,10 +10626,7 @@ function readMemoriesFtsSql(db) {
|
|
|
10719
10626
|
function memoriesFtsNeedsTokenizerRepair(sql) {
|
|
10720
10627
|
if (sql === null)
|
|
10721
10628
|
return false;
|
|
10722
|
-
|
|
10723
|
-
if (normalized.includes("porter unicode61"))
|
|
10724
|
-
return true;
|
|
10725
|
-
return !normalized.includes(`tokenize='${MEMORIES_FTS_TOKENIZER}'`);
|
|
10629
|
+
return !normalizeSql(sql).includes(`tokenize='${MEMORIES_FTS_TOKENIZER}'`);
|
|
10726
10630
|
}
|
|
10727
10631
|
function up6(db) {
|
|
10728
10632
|
db.exec(`
|
|
@@ -14635,38 +14539,6 @@ function up129(db) {
|
|
|
14635
14539
|
ON imported_source_lifecycle(agent_id, status, updated_at DESC);
|
|
14636
14540
|
`);
|
|
14637
14541
|
}
|
|
14638
|
-
function tableExists2(db, table) {
|
|
14639
|
-
return db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(table) != null;
|
|
14640
|
-
}
|
|
14641
|
-
function hasColumn24(db, table, column) {
|
|
14642
|
-
return db.prepare(`PRAGMA table_info("${table}")`).all().some((row) => row.name === column);
|
|
14643
|
-
}
|
|
14644
|
-
function backfill(db, rows, sourceKind, scannedAt) {
|
|
14645
|
-
const statement = db.prepare(`INSERT INTO memory_content_safety
|
|
14646
|
-
(agent_id, source_kind, source_id, status, context_eligible, reasons_json, policy_version, scanned_at)
|
|
14647
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
14648
|
-
ON CONFLICT(agent_id, source_kind, source_id) DO UPDATE SET
|
|
14649
|
-
status = excluded.status,
|
|
14650
|
-
context_eligible = excluded.context_eligible,
|
|
14651
|
-
reasons_json = excluded.reasons_json,
|
|
14652
|
-
policy_version = excluded.policy_version,
|
|
14653
|
-
scanned_at = excluded.scanned_at`);
|
|
14654
|
-
for (const row of rows) {
|
|
14655
|
-
const sourceId = row.source_id?.trim();
|
|
14656
|
-
if (!sourceId)
|
|
14657
|
-
continue;
|
|
14658
|
-
const assessment = scanMemoryContent(row.content ?? "");
|
|
14659
|
-
statement.run(row.agent_id?.trim() || "default", sourceKind, sourceId, assessment.status, assessment.contextEligible ? 1 : 0, JSON.stringify(assessment.reasons), MEMORY_CONTENT_SAFETY_POLICY_VERSION, scannedAt);
|
|
14660
|
-
}
|
|
14661
|
-
}
|
|
14662
|
-
function backfillTable(db, params) {
|
|
14663
|
-
if (!tableExists2(db, params.table) || !hasColumn24(db, params.table, params.sourceIdColumn) || !hasColumn24(db, params.table, params.contentColumn))
|
|
14664
|
-
return;
|
|
14665
|
-
const agentColumn = hasColumn24(db, params.table, "agent_id") ? "COALESCE(NULLIF(TRIM(agent_id), ''), 'default')" : "'default'";
|
|
14666
|
-
const rows = db.prepare(`SELECT ${agentColumn} AS agent_id, ${params.sourceIdColumn} AS source_id, ${params.contentColumn} AS content
|
|
14667
|
-
FROM ${params.table}${params.where ? ` WHERE ${params.where}` : ""}`).all();
|
|
14668
|
-
backfill(db, rows, params.sourceKind, params.scannedAt);
|
|
14669
|
-
}
|
|
14670
14542
|
function up130(db) {
|
|
14671
14543
|
db.exec(`
|
|
14672
14544
|
CREATE TABLE IF NOT EXISTS memory_content_safety (
|
|
@@ -14686,43 +14558,6 @@ function up130(db) {
|
|
|
14686
14558
|
CREATE INDEX IF NOT EXISTS idx_memory_content_safety_eligibility
|
|
14687
14559
|
ON memory_content_safety(agent_id, source_kind, context_eligible);
|
|
14688
14560
|
`);
|
|
14689
|
-
const scannedAt = new Date().toISOString();
|
|
14690
|
-
backfillTable(db, {
|
|
14691
|
-
table: "memories",
|
|
14692
|
-
sourceKind: "memory",
|
|
14693
|
-
sourceIdColumn: "id",
|
|
14694
|
-
contentColumn: "content",
|
|
14695
|
-
scannedAt
|
|
14696
|
-
});
|
|
14697
|
-
backfillTable(db, {
|
|
14698
|
-
table: "memory_artifacts",
|
|
14699
|
-
sourceKind: "artifact",
|
|
14700
|
-
sourceIdColumn: "source_path",
|
|
14701
|
-
contentColumn: "content",
|
|
14702
|
-
scannedAt
|
|
14703
|
-
});
|
|
14704
|
-
backfillTable(db, {
|
|
14705
|
-
table: "session_transcripts",
|
|
14706
|
-
sourceKind: "transcript",
|
|
14707
|
-
sourceIdColumn: "session_key",
|
|
14708
|
-
contentColumn: "content",
|
|
14709
|
-
scannedAt
|
|
14710
|
-
});
|
|
14711
|
-
backfillTable(db, {
|
|
14712
|
-
table: "session_summaries",
|
|
14713
|
-
sourceKind: "summary",
|
|
14714
|
-
sourceIdColumn: "id",
|
|
14715
|
-
contentColumn: "content",
|
|
14716
|
-
scannedAt
|
|
14717
|
-
});
|
|
14718
|
-
backfillTable(db, {
|
|
14719
|
-
table: "embeddings",
|
|
14720
|
-
sourceKind: "source_chunk",
|
|
14721
|
-
sourceIdColumn: "id",
|
|
14722
|
-
contentColumn: "chunk_text",
|
|
14723
|
-
where: "source_type IN ('source_chunk', 'source_obsidian_chunk')",
|
|
14724
|
-
scannedAt
|
|
14725
|
-
});
|
|
14726
14561
|
}
|
|
14727
14562
|
function up131(db) {
|
|
14728
14563
|
const table = db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'dreaming_attention'").get();
|
|
@@ -14826,13 +14661,13 @@ function up133(db) {
|
|
|
14826
14661
|
AND error IS NOT NULL;
|
|
14827
14662
|
`);
|
|
14828
14663
|
}
|
|
14829
|
-
function
|
|
14664
|
+
function tableExists2(db, table) {
|
|
14830
14665
|
return db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(table) != null;
|
|
14831
14666
|
}
|
|
14832
14667
|
function up134(db) {
|
|
14833
|
-
if (!
|
|
14668
|
+
if (!tableExists2(db, "memory_jobs"))
|
|
14834
14669
|
return;
|
|
14835
|
-
if (!
|
|
14670
|
+
if (!tableExists2(db, "job_cancellations")) {
|
|
14836
14671
|
throw new Error("job_cancellations table missing; cannot retire structural jobs without an audit trail");
|
|
14837
14672
|
}
|
|
14838
14673
|
db.prepare(`
|
|
@@ -15061,12 +14896,12 @@ function up142(db) {
|
|
|
15061
14896
|
if (!cols.has(name))
|
|
15062
14897
|
db.exec(`ALTER TABLE dreaming_passes ADD COLUMN ${name} ${type}`);
|
|
15063
14898
|
}
|
|
15064
|
-
function
|
|
14899
|
+
function hasColumn24(db, table, column) {
|
|
15065
14900
|
const rows = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
15066
14901
|
return rows.some((row) => row.name === column);
|
|
15067
14902
|
}
|
|
15068
14903
|
function addColumnIfMissing28(db, table, column, definition) {
|
|
15069
|
-
if (!
|
|
14904
|
+
if (!hasColumn24(db, table, column))
|
|
15070
14905
|
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
|
|
15071
14906
|
}
|
|
15072
14907
|
function up143(db) {
|
|
@@ -15938,6 +15773,138 @@ function up162(db) {
|
|
|
15938
15773
|
END;
|
|
15939
15774
|
`);
|
|
15940
15775
|
}
|
|
15776
|
+
function up163(db) {
|
|
15777
|
+
db.exec(`
|
|
15778
|
+
DROP TRIGGER IF EXISTS memory_content_safety_head_freshness_ai;
|
|
15779
|
+
DROP TRIGGER IF EXISTS memory_content_safety_head_freshness_au;
|
|
15780
|
+
DROP INDEX IF EXISTS idx_memory_content_safety_status;
|
|
15781
|
+
DROP INDEX IF EXISTS idx_memory_content_safety_eligibility;
|
|
15782
|
+
DROP TABLE IF EXISTS memory_content_safety;
|
|
15783
|
+
`);
|
|
15784
|
+
}
|
|
15785
|
+
var COLUMNS3 = ["occurred_start", "occurred_end", "valid_from", "valid_until", "time_precision"];
|
|
15786
|
+
function up164(db) {
|
|
15787
|
+
const cols = db.prepare("PRAGMA table_info(entity_attributes)").all();
|
|
15788
|
+
for (const column of COLUMNS3) {
|
|
15789
|
+
if (!cols.some((col) => col.name === column)) {
|
|
15790
|
+
db.exec(`ALTER TABLE entity_attributes ADD COLUMN ${column} TEXT`);
|
|
15791
|
+
}
|
|
15792
|
+
}
|
|
15793
|
+
}
|
|
15794
|
+
function up165(db) {
|
|
15795
|
+
const passColumns = db.prepare("PRAGMA table_info(dreaming_passes)").all();
|
|
15796
|
+
if (!passColumns.some((column) => column.name === "scope_key")) {
|
|
15797
|
+
db.exec("ALTER TABLE dreaming_passes ADD COLUMN scope_key TEXT");
|
|
15798
|
+
}
|
|
15799
|
+
db.exec(`
|
|
15800
|
+
CREATE TABLE IF NOT EXISTS dreaming_history_nodes (
|
|
15801
|
+
agent_id TEXT NOT NULL,
|
|
15802
|
+
scope_key TEXT NOT NULL,
|
|
15803
|
+
level INTEGER NOT NULL,
|
|
15804
|
+
idx INTEGER NOT NULL,
|
|
15805
|
+
pass_id TEXT,
|
|
15806
|
+
text TEXT NOT NULL,
|
|
15807
|
+
tokens_input INTEGER,
|
|
15808
|
+
tokens_output INTEGER,
|
|
15809
|
+
tokens_cache_read INTEGER,
|
|
15810
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
15811
|
+
PRIMARY KEY (agent_id, scope_key, level, idx)
|
|
15812
|
+
);
|
|
15813
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_dreaming_history_nodes_pass
|
|
15814
|
+
ON dreaming_history_nodes(pass_id) WHERE pass_id IS NOT NULL;
|
|
15815
|
+
`);
|
|
15816
|
+
}
|
|
15817
|
+
function up166(db) {
|
|
15818
|
+
const columns = db.prepare("PRAGMA table_info(dreaming_passes)").all();
|
|
15819
|
+
if (!columns.some((column) => column.name === "tokens_peak_context")) {
|
|
15820
|
+
db.exec("ALTER TABLE dreaming_passes ADD COLUMN tokens_peak_context INTEGER");
|
|
15821
|
+
}
|
|
15822
|
+
}
|
|
15823
|
+
function up167(db) {
|
|
15824
|
+
const sql = readMemoriesFtsSql(db);
|
|
15825
|
+
if (sql !== null && !memoriesFtsNeedsTokenizerRepair(sql))
|
|
15826
|
+
return;
|
|
15827
|
+
recreateMemoriesFts(db);
|
|
15828
|
+
}
|
|
15829
|
+
function hasTable5(db, table) {
|
|
15830
|
+
return db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(table) != null;
|
|
15831
|
+
}
|
|
15832
|
+
function hasColumn25(db, table, column) {
|
|
15833
|
+
return db.prepare(`PRAGMA table_info(${table})`).all().some((row) => row.name === column);
|
|
15834
|
+
}
|
|
15835
|
+
function up168(db) {
|
|
15836
|
+
if (!hasTable5(db, "entity_attributes") || !hasTable5(db, "entity_aspects") || !hasTable5(db, "entities") || !hasColumn25(db, "entity_attributes", "source_root") || !hasColumn25(db, "entities", "source_root")) {
|
|
15837
|
+
return;
|
|
15838
|
+
}
|
|
15839
|
+
const now = new Date().toISOString();
|
|
15840
|
+
db.exec(`
|
|
15841
|
+
CREATE TEMP TABLE retired_source_paragraphs AS
|
|
15842
|
+
SELECT attr.id, attr.memory_id, attr.agent_id, asp.entity_id
|
|
15843
|
+
FROM entity_attributes attr
|
|
15844
|
+
JOIN entity_aspects asp ON asp.id = attr.aspect_id
|
|
15845
|
+
JOIN entities e ON e.id = asp.entity_id
|
|
15846
|
+
WHERE e.entity_type IN ('source_document', 'source_document_reference')
|
|
15847
|
+
AND attr.source_root IS NOT NULL
|
|
15848
|
+
AND attr.source_root = e.source_root
|
|
15849
|
+
AND attr.source_id = e.source_id
|
|
15850
|
+
AND attr.source_root NOT IN ('dreaming', 'dreaming_attention')
|
|
15851
|
+
`);
|
|
15852
|
+
if (hasColumn25(db, "entity_attributes", "memory_id") && hasTable5(db, "memories")) {
|
|
15853
|
+
if (hasTable5(db, "memory_history")) {
|
|
15854
|
+
db.prepare(`INSERT INTO memory_history (id, memory_id, event, old_content, new_content, changed_by, reason, metadata, created_at)
|
|
15855
|
+
SELECT lower(hex(randomblob(16))), mem.id, 'deleted', mem.content, NULL, 'migration:168',
|
|
15856
|
+
'Source paragraph claims retired', '{"force":true}', ?
|
|
15857
|
+
FROM memories mem
|
|
15858
|
+
JOIN retired_source_paragraphs retired ON retired.memory_id = mem.id
|
|
15859
|
+
WHERE mem.is_deleted = 0`).run(now);
|
|
15860
|
+
}
|
|
15861
|
+
if (hasTable5(db, "derived_memory_sources") && hasColumn25(db, "memories", "stale_at")) {
|
|
15862
|
+
db.prepare(`UPDATE memories
|
|
15863
|
+
SET stale_at = ?
|
|
15864
|
+
WHERE stale_at IS NULL
|
|
15865
|
+
AND is_deleted = 0
|
|
15866
|
+
AND id IN (
|
|
15867
|
+
SELECT dms.derived_memory_id
|
|
15868
|
+
FROM derived_memory_sources dms
|
|
15869
|
+
JOIN retired_source_paragraphs retired ON retired.agent_id = dms.agent_id
|
|
15870
|
+
WHERE (dms.source_kind = 'memory' AND dms.source_id = retired.memory_id)
|
|
15871
|
+
OR (dms.source_kind = 'ontology_claim' AND dms.source_id = retired.id)
|
|
15872
|
+
)`).run(now);
|
|
15873
|
+
}
|
|
15874
|
+
const forget = [
|
|
15875
|
+
"is_deleted = 1",
|
|
15876
|
+
...["deleted_at", "updated_at"].filter((column) => hasColumn25(db, "memories", column)).map((column) => `${column} = $now`),
|
|
15877
|
+
...hasColumn25(db, "memories", "updated_by") ? ["updated_by = 'migration:168'"] : [],
|
|
15878
|
+
...hasColumn25(db, "memories", "version") ? ["version = version + 1"] : []
|
|
15879
|
+
];
|
|
15880
|
+
db.prepare(`UPDATE memories
|
|
15881
|
+
SET ${forget.join(", ")}
|
|
15882
|
+
WHERE is_deleted = 0
|
|
15883
|
+
AND id IN (SELECT memory_id FROM retired_source_paragraphs WHERE memory_id IS NOT NULL)`).run({ $now: now });
|
|
15884
|
+
if (hasTable5(db, "memory_entity_mentions")) {
|
|
15885
|
+
db.exec(`
|
|
15886
|
+
DELETE FROM memory_entity_mentions
|
|
15887
|
+
WHERE memory_id IN (SELECT memory_id FROM retired_source_paragraphs WHERE memory_id IS NOT NULL)
|
|
15888
|
+
`);
|
|
15889
|
+
}
|
|
15890
|
+
}
|
|
15891
|
+
if (hasTable5(db, "ontology_contradictions")) {
|
|
15892
|
+
db.prepare(`UPDATE ontology_contradictions
|
|
15893
|
+
SET status = 'resolved', resolved_at = ?, resolution_reason = 'source paragraph claims retired', updated_at = ?
|
|
15894
|
+
WHERE status = 'active'
|
|
15895
|
+
AND (left_attribute_id IN (SELECT id FROM retired_source_paragraphs)
|
|
15896
|
+
OR right_attribute_id IN (SELECT id FROM retired_source_paragraphs))`).run(now, now);
|
|
15897
|
+
}
|
|
15898
|
+
db.exec(`
|
|
15899
|
+
DELETE FROM entity_attributes WHERE id IN (SELECT id FROM retired_source_paragraphs);
|
|
15900
|
+
DELETE FROM entity_aspects
|
|
15901
|
+
WHERE entity_id IN (
|
|
15902
|
+
SELECT id FROM entities WHERE entity_type IN ('source_document', 'source_document_reference')
|
|
15903
|
+
)
|
|
15904
|
+
AND NOT EXISTS (SELECT 1 FROM entity_attributes attr WHERE attr.aspect_id = entity_aspects.id);
|
|
15905
|
+
DROP TABLE retired_source_paragraphs;
|
|
15906
|
+
`);
|
|
15907
|
+
}
|
|
15941
15908
|
var MIGRATIONS = [
|
|
15942
15909
|
{
|
|
15943
15910
|
version: 1,
|
|
@@ -16947,8 +16914,7 @@ var MIGRATIONS = [
|
|
|
16947
16914
|
{
|
|
16948
16915
|
version: 125,
|
|
16949
16916
|
name: "memory-content-safety",
|
|
16950
|
-
up: up130
|
|
16951
|
-
artifacts: { tables: ["memory_content_safety"] }
|
|
16917
|
+
up: up130
|
|
16952
16918
|
},
|
|
16953
16919
|
{
|
|
16954
16920
|
version: 126,
|
|
@@ -17250,6 +17216,48 @@ var MIGRATIONS = [
|
|
|
17250
17216
|
name: "generic-entity-prune-scan-generation",
|
|
17251
17217
|
up: up162,
|
|
17252
17218
|
artifacts: { tables: ["generic_entity_prune_scan_state"] }
|
|
17219
|
+
},
|
|
17220
|
+
{
|
|
17221
|
+
version: 163,
|
|
17222
|
+
name: "retire-memory-content-safety",
|
|
17223
|
+
up: up163
|
|
17224
|
+
},
|
|
17225
|
+
{
|
|
17226
|
+
version: 164,
|
|
17227
|
+
name: "claim-event-time",
|
|
17228
|
+
up: up164,
|
|
17229
|
+
artifacts: {
|
|
17230
|
+
columns: ["occurred_start", "occurred_end", "valid_from", "valid_until", "time_precision"].map((column) => ({
|
|
17231
|
+
table: "entity_attributes",
|
|
17232
|
+
column
|
|
17233
|
+
}))
|
|
17234
|
+
}
|
|
17235
|
+
},
|
|
17236
|
+
{
|
|
17237
|
+
version: 165,
|
|
17238
|
+
name: "dreaming-history",
|
|
17239
|
+
up: up165,
|
|
17240
|
+
artifacts: {
|
|
17241
|
+
tables: ["dreaming_history_nodes"],
|
|
17242
|
+
indexes: ["idx_dreaming_history_nodes_pass"],
|
|
17243
|
+
columns: [{ table: "dreaming_passes", column: "scope_key" }]
|
|
17244
|
+
}
|
|
17245
|
+
},
|
|
17246
|
+
{
|
|
17247
|
+
version: 166,
|
|
17248
|
+
name: "dreaming-pass-peak-context",
|
|
17249
|
+
up: up166,
|
|
17250
|
+
artifacts: { columns: [{ table: "dreaming_passes", column: "tokens_peak_context" }] }
|
|
17251
|
+
},
|
|
17252
|
+
{
|
|
17253
|
+
version: 167,
|
|
17254
|
+
name: "memories-fts-porter",
|
|
17255
|
+
up: up167
|
|
17256
|
+
},
|
|
17257
|
+
{
|
|
17258
|
+
version: 168,
|
|
17259
|
+
name: "retire-source-paragraph-claims",
|
|
17260
|
+
up: up168
|
|
17253
17261
|
}
|
|
17254
17262
|
];
|
|
17255
17263
|
var LATEST_SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1]?.version ?? 0;
|
|
@@ -17491,6 +17499,37 @@ var PIPELINE_PROVIDER_CHOICES = [
|
|
|
17491
17499
|
var SYNTHESIS_PROVIDER_CHOICES = PIPELINE_PROVIDER_CHOICES.filter((provider) => provider !== "command");
|
|
17492
17500
|
var PIPELINE_PROVIDER_SET = new Set(PIPELINE_PROVIDER_CHOICES);
|
|
17493
17501
|
var SYNTHESIS_PROVIDER_SET = new Set(SYNTHESIS_PROVIDER_CHOICES);
|
|
17502
|
+
var SECRET_NAMES = "(?:access[_-]?token|api[_-]?key|apikey|auth[_-]?token|client[_-]?secret|passw(?:or)?d|refresh[_-]?token|private[_-]?key|secret(?:[_-]?key)?|token)";
|
|
17503
|
+
var SECRET_VALUE = `(?=[^\\s"',;&<>()\\[\\]{}$]*\\d)[^\\s"',;&<>()\\[\\]{}$]{8,}`;
|
|
17504
|
+
var PATTERNS = [
|
|
17505
|
+
{
|
|
17506
|
+
kind: "private_key",
|
|
17507
|
+
pattern: /-----BEGIN (?:[A-Z]+ )*PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z]+ )*PRIVATE KEY-----/g
|
|
17508
|
+
},
|
|
17509
|
+
{
|
|
17510
|
+
kind: "provider_key",
|
|
17511
|
+
pattern: /\b(?:(?:AKIA|ASIA)[0-9A-Z]{16}|github_pat_[A-Za-z0-9_]{20,}|gh[pousr]_[A-Za-z0-9]{20,}|sk-(?:ant-|proj-)?[A-Za-z0-9_-]{20,}|(?:sk|rk)_(?:live|test)_[0-9A-Za-z]{16,}|xox[abprs]-[A-Za-z0-9-]{10,}|AIza[0-9A-Za-z_-]{35}|glpat-[A-Za-z0-9_-]{20,}|npm_[A-Za-z0-9]{36})\b/g
|
|
17512
|
+
},
|
|
17513
|
+
{
|
|
17514
|
+
kind: "jwt",
|
|
17515
|
+
pattern: /\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g
|
|
17516
|
+
},
|
|
17517
|
+
{
|
|
17518
|
+
kind: "bearer_token",
|
|
17519
|
+
pattern: /\bBearer\s+([A-Za-z0-9._~+/-]{16,}=*)/g,
|
|
17520
|
+
valueGroup: 1
|
|
17521
|
+
},
|
|
17522
|
+
{
|
|
17523
|
+
kind: "secret_assignment",
|
|
17524
|
+
pattern: new RegExp(`\\b${SECRET_NAMES}["']?\\s*[:=]\\s*["']?(${SECRET_VALUE})`, "gi"),
|
|
17525
|
+
valueGroup: 1
|
|
17526
|
+
},
|
|
17527
|
+
{
|
|
17528
|
+
kind: "secret_assignment",
|
|
17529
|
+
pattern: new RegExp(`\\b[A-Z][A-Z0-9_]*(?:API_KEY|TOKEN|SECRET|PASSWORD)\\s*=\\s*["']?(${SECRET_VALUE})`, "g"),
|
|
17530
|
+
valueGroup: 1
|
|
17531
|
+
}
|
|
17532
|
+
];
|
|
17494
17533
|
var LOOPBACK_HOST = "127.0.0.1";
|
|
17495
17534
|
function normalizeLoopbackHost(host) {
|
|
17496
17535
|
const normalized = host.trim().toLowerCase().replace(/^\[|\]$/g, "");
|
package/dist/index.js
CHANGED
|
@@ -10395,99 +10395,6 @@ var execFileHiddenImpl = (...args) => Reflect.apply(nodeExecFile, undefined, wit
|
|
|
10395
10395
|
Object.defineProperty(execFileHiddenImpl, promisify.custom, {
|
|
10396
10396
|
value: (...args) => Reflect.apply(nodeExecFileAsync, undefined, withWindowsHideArgument(args, 1))
|
|
10397
10397
|
});
|
|
10398
|
-
var MEMORY_CONTENT_SAFETY_POLICY_VERSION = "memory-content-safety-v1";
|
|
10399
|
-
var MEMORY_CONTENT_SAFETY_REASONS = [
|
|
10400
|
-
"prompt_injection",
|
|
10401
|
-
"exfiltration",
|
|
10402
|
-
"credential_harvesting",
|
|
10403
|
-
"malicious_shell",
|
|
10404
|
-
"tool_directive",
|
|
10405
|
-
"invisible_unicode"
|
|
10406
|
-
];
|
|
10407
|
-
var INVISIBLE_UNICODE_RE = /(?:\u034f|[\u00ad\u061c\u070f\u180e\u200b\u200c\u200e\u200f\u202a-\u202e\u2060\u2066-\u2069\u206a-\u206f\ufeff]|[\u{e0000}-\u{e007f}])/u;
|
|
10408
|
-
var STRONG_DEFENSIVE_CONTEXT_RE = /\b(?:security\s+(?:guidance|discussion|analysis)|threat\s+model|defensive)\b/i;
|
|
10409
|
-
var REPORTING_CONTEXT_RE = /\b(?:example|illustrat\w*|sample|quote|quoted|detector|scanner|classif\w*)\b/i;
|
|
10410
|
-
var REPORTING_BEFORE_RE = /\b(?:example|illustrat\w*|sample|quote|quoted|detector|scanner|classif\w*)\b[\s\S]{0,80}\b(?:say\w*|read\w*|show\w*|flag\w*|detect\w*|describ\w*|demonstrat\w*|contain\w*|match\w*|pattern)\b/i;
|
|
10411
|
-
var REPORTING_AFTER_RE = /\b(?:detector|scanner|classif\w*|flag\w*|pattern|dangerous|unsafe|malicious|hostile|should|would|must|never|do not|don't|avoid|quoted)\b/i;
|
|
10412
|
-
var NEGATED_DIRECTIVE_RE = /\b(?:never|do not|don't|should not|must not|cannot|can't|avoid|prevent|detect|mitigat\w*)\b[\s\S]{0,80}$/i;
|
|
10413
|
-
var PROMPT_INJECTION_RES = [
|
|
10414
|
-
/\b(?:ignore|disregard|override|forget|bypass)\b[\s\S]{0,100}\b(?:previous|prior|above|earlier|system|developer|assistant|safety|security)?\s*(?:instructions?|rules?|prompt|message)\b/i,
|
|
10415
|
-
/\b(?:new|following|these)\s+(?:(?:system|developer|assistant|hidden)\s+)?instructions?\b/i,
|
|
10416
|
-
/(?:^|\n)\s*(?:system|developer|instruction|prompt)\s*:/im,
|
|
10417
|
-
/<\s*(?:system|developer|assistant|instruction|prompt)\b[^>]*>/i,
|
|
10418
|
-
/\b(?:you are now|act as|roleplay as|pretend to be)\b[\s\S]{0,80}\b(?:system|admin|developer|unrestricted|jailbreak|different agent)\b/i
|
|
10419
|
-
];
|
|
10420
|
-
var TOOL_DIRECTIVE_RES = [
|
|
10421
|
-
/<\s*(?:tool[_-]?call|function[_-]?call|invoke|tool)\b/i,
|
|
10422
|
-
/\b(?:assistant|system)\s+to\s*=\s*[a-z0-9_.-]+/i,
|
|
10423
|
-
/\b(?:call|invoke|use|run|execute)\s+(?:the\s+)?[a-z0-9_.-]+\s+tool\b/i
|
|
10424
|
-
];
|
|
10425
|
-
var EXFILTRATION_RE = /\b(?:reveal|show|print|send|upload|exfiltrat\w*|dump|forward|leak|transmit|export)\b[\s\S]{0,120}(?:\b(?:system\s+prompt|hidden\s+instructions?|secret(?:s)?|credential(?:s)?|password(?:s)?|api\s*keys?|tokens?|private\s+keys?|environment\s+variables?)\b|\.env\b|~\/(?:\.ssh)\/\S+|\/etc\/(?:shadow|passwd)\b)/i;
|
|
10426
|
-
var EXFILTRATION_REVERSE = /(?:\b(?:system\s+prompt|hidden\s+instructions?|secret(?:s)?|credential(?:s)?|password(?:s)?|api\s*keys?|tokens?|private\s+keys?|environment\s+variables?)\b|\.env\b|~\/(?:\.ssh)\/\S+|\/etc\/(?:shadow|passwd)\b)[\s\S]{0,120}\b(?:reveal|show|print|send|upload|exfiltrat\w*|dump|forward|leak|transmit|export)\b/i;
|
|
10427
|
-
var CREDENTIAL_HARVESTING_RE = /\b(?:enter|paste|provide|share|send|give|submit|type|hand over)\b[\s\S]{0,80}\b(?:password|api\s*key|token|secret|credential|private\s+key)\b/i;
|
|
10428
|
-
var DANGEROUS_SHELL_RE = /\b(?:curl|wget)\b[^\n]{0,240}\|\s*(?:ba|z|fi)?sh\b|\brm\s+-rf\s+(?:\/|~|\.ssh)[^\n]{0,240}|\b(?:cat|head|tail)\s+~\/?\.ssh\/(?:id_[a-z]+|authorized_keys)\b|\b(?:printenv|env)\b[^\n]{0,120}\b(?:curl|wget|send|upload|post)\b/i;
|
|
10429
|
-
function matchHasDefensiveContext(content, match) {
|
|
10430
|
-
if (!match || !match[0])
|
|
10431
|
-
return false;
|
|
10432
|
-
const start = match.index ?? 0;
|
|
10433
|
-
const before = content.slice(Math.max(0, start - 120), start);
|
|
10434
|
-
const after = content.slice(start + match[0].length, start + match[0].length + 160);
|
|
10435
|
-
return STRONG_DEFENSIVE_CONTEXT_RE.test(match[0]) || NEGATED_DIRECTIVE_RE.test(before) || STRONG_DEFENSIVE_CONTEXT_RE.test(before) || STRONG_DEFENSIVE_CONTEXT_RE.test(after) || REPORTING_BEFORE_RE.test(before) || REPORTING_CONTEXT_RE.test(before) && REPORTING_AFTER_RE.test(after);
|
|
10436
|
-
}
|
|
10437
|
-
function hasActionableMatch(content, patterns) {
|
|
10438
|
-
return patterns.some((pattern) => {
|
|
10439
|
-
const flags = pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`;
|
|
10440
|
-
const searchable = new RegExp(pattern.source, flags);
|
|
10441
|
-
let match;
|
|
10442
|
-
while ((match = searchable.exec(content)) !== null) {
|
|
10443
|
-
if (!matchHasDefensiveContext(content, match))
|
|
10444
|
-
return true;
|
|
10445
|
-
if (match[0].length === 0)
|
|
10446
|
-
searchable.lastIndex += 1;
|
|
10447
|
-
}
|
|
10448
|
-
return false;
|
|
10449
|
-
});
|
|
10450
|
-
}
|
|
10451
|
-
function hasDangerousShell(content) {
|
|
10452
|
-
const searchable = new RegExp(DANGEROUS_SHELL_RE.source, `${DANGEROUS_SHELL_RE.flags}g`);
|
|
10453
|
-
let match;
|
|
10454
|
-
while ((match = searchable.exec(content)) !== null) {
|
|
10455
|
-
if (matchHasDefensiveContext(content, match))
|
|
10456
|
-
continue;
|
|
10457
|
-
const end = (match.index ?? 0) + match[0].length;
|
|
10458
|
-
const after = content.slice(end, end + 160);
|
|
10459
|
-
if (!STRONG_DEFENSIVE_CONTEXT_RE.test(after) && !REPORTING_AFTER_RE.test(after))
|
|
10460
|
-
return true;
|
|
10461
|
-
if (match[0].length === 0)
|
|
10462
|
-
searchable.lastIndex += 1;
|
|
10463
|
-
}
|
|
10464
|
-
return false;
|
|
10465
|
-
}
|
|
10466
|
-
function scanMemoryContent(content) {
|
|
10467
|
-
const raw = typeof content === "string" ? content : String(content ?? "");
|
|
10468
|
-
const normalized = raw.normalize("NFKC");
|
|
10469
|
-
const reasons = new Set;
|
|
10470
|
-
if (INVISIBLE_UNICODE_RE.test(raw))
|
|
10471
|
-
reasons.add("invisible_unicode");
|
|
10472
|
-
if (hasActionableMatch(normalized, PROMPT_INJECTION_RES))
|
|
10473
|
-
reasons.add("prompt_injection");
|
|
10474
|
-
if (hasActionableMatch(normalized, TOOL_DIRECTIVE_RES))
|
|
10475
|
-
reasons.add("tool_directive");
|
|
10476
|
-
if (hasActionableMatch(normalized, [EXFILTRATION_RE, EXFILTRATION_REVERSE]))
|
|
10477
|
-
reasons.add("exfiltration");
|
|
10478
|
-
if (hasActionableMatch(normalized, [CREDENTIAL_HARVESTING_RE]))
|
|
10479
|
-
reasons.add("credential_harvesting");
|
|
10480
|
-
if (hasDangerousShell(normalized))
|
|
10481
|
-
reasons.add("malicious_shell");
|
|
10482
|
-
const orderedReasons = MEMORY_CONTENT_SAFETY_REASONS.filter((reason) => reasons.has(reason));
|
|
10483
|
-
const status = orderedReasons.length === 0 ? "clean" : orderedReasons.every((reason) => reason === "invisible_unicode") ? "tainted" : "blocked";
|
|
10484
|
-
return {
|
|
10485
|
-
status,
|
|
10486
|
-
contextEligible: status === "clean",
|
|
10487
|
-
reasons: orderedReasons,
|
|
10488
|
-
policyVersion: MEMORY_CONTENT_SAFETY_POLICY_VERSION
|
|
10489
|
-
};
|
|
10490
|
-
}
|
|
10491
10398
|
var DAEMON_DERIVED_MEMORY_SOURCE_TYPES = [
|
|
10492
10399
|
"extract",
|
|
10493
10400
|
"aggregate-recall",
|
|
@@ -10610,7 +10517,7 @@ function up4(db) {
|
|
|
10610
10517
|
function up5(db) {
|
|
10611
10518
|
db.exec("DROP TABLE IF EXISTS mcp_invocations");
|
|
10612
10519
|
}
|
|
10613
|
-
var MEMORIES_FTS_TOKENIZER = "unicode61";
|
|
10520
|
+
var MEMORIES_FTS_TOKENIZER = "porter unicode61";
|
|
10614
10521
|
var FTS_STATE_TABLE = "memories_fts_state";
|
|
10615
10522
|
function normalizeSql(sql) {
|
|
10616
10523
|
return sql.replace(/\s+/g, " ").trim().toLowerCase();
|
|
@@ -10720,10 +10627,7 @@ function readMemoriesFtsSql(db) {
|
|
|
10720
10627
|
function memoriesFtsNeedsTokenizerRepair(sql) {
|
|
10721
10628
|
if (sql === null)
|
|
10722
10629
|
return false;
|
|
10723
|
-
|
|
10724
|
-
if (normalized.includes("porter unicode61"))
|
|
10725
|
-
return true;
|
|
10726
|
-
return !normalized.includes(`tokenize='${MEMORIES_FTS_TOKENIZER}'`);
|
|
10630
|
+
return !normalizeSql(sql).includes(`tokenize='${MEMORIES_FTS_TOKENIZER}'`);
|
|
10727
10631
|
}
|
|
10728
10632
|
function up6(db) {
|
|
10729
10633
|
db.exec(`
|
|
@@ -14636,38 +14540,6 @@ function up129(db) {
|
|
|
14636
14540
|
ON imported_source_lifecycle(agent_id, status, updated_at DESC);
|
|
14637
14541
|
`);
|
|
14638
14542
|
}
|
|
14639
|
-
function tableExists2(db, table) {
|
|
14640
|
-
return db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(table) != null;
|
|
14641
|
-
}
|
|
14642
|
-
function hasColumn24(db, table, column) {
|
|
14643
|
-
return db.prepare(`PRAGMA table_info("${table}")`).all().some((row) => row.name === column);
|
|
14644
|
-
}
|
|
14645
|
-
function backfill(db, rows, sourceKind, scannedAt) {
|
|
14646
|
-
const statement = db.prepare(`INSERT INTO memory_content_safety
|
|
14647
|
-
(agent_id, source_kind, source_id, status, context_eligible, reasons_json, policy_version, scanned_at)
|
|
14648
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
14649
|
-
ON CONFLICT(agent_id, source_kind, source_id) DO UPDATE SET
|
|
14650
|
-
status = excluded.status,
|
|
14651
|
-
context_eligible = excluded.context_eligible,
|
|
14652
|
-
reasons_json = excluded.reasons_json,
|
|
14653
|
-
policy_version = excluded.policy_version,
|
|
14654
|
-
scanned_at = excluded.scanned_at`);
|
|
14655
|
-
for (const row of rows) {
|
|
14656
|
-
const sourceId = row.source_id?.trim();
|
|
14657
|
-
if (!sourceId)
|
|
14658
|
-
continue;
|
|
14659
|
-
const assessment = scanMemoryContent(row.content ?? "");
|
|
14660
|
-
statement.run(row.agent_id?.trim() || "default", sourceKind, sourceId, assessment.status, assessment.contextEligible ? 1 : 0, JSON.stringify(assessment.reasons), MEMORY_CONTENT_SAFETY_POLICY_VERSION, scannedAt);
|
|
14661
|
-
}
|
|
14662
|
-
}
|
|
14663
|
-
function backfillTable(db, params) {
|
|
14664
|
-
if (!tableExists2(db, params.table) || !hasColumn24(db, params.table, params.sourceIdColumn) || !hasColumn24(db, params.table, params.contentColumn))
|
|
14665
|
-
return;
|
|
14666
|
-
const agentColumn = hasColumn24(db, params.table, "agent_id") ? "COALESCE(NULLIF(TRIM(agent_id), ''), 'default')" : "'default'";
|
|
14667
|
-
const rows = db.prepare(`SELECT ${agentColumn} AS agent_id, ${params.sourceIdColumn} AS source_id, ${params.contentColumn} AS content
|
|
14668
|
-
FROM ${params.table}${params.where ? ` WHERE ${params.where}` : ""}`).all();
|
|
14669
|
-
backfill(db, rows, params.sourceKind, params.scannedAt);
|
|
14670
|
-
}
|
|
14671
14543
|
function up130(db) {
|
|
14672
14544
|
db.exec(`
|
|
14673
14545
|
CREATE TABLE IF NOT EXISTS memory_content_safety (
|
|
@@ -14687,43 +14559,6 @@ function up130(db) {
|
|
|
14687
14559
|
CREATE INDEX IF NOT EXISTS idx_memory_content_safety_eligibility
|
|
14688
14560
|
ON memory_content_safety(agent_id, source_kind, context_eligible);
|
|
14689
14561
|
`);
|
|
14690
|
-
const scannedAt = new Date().toISOString();
|
|
14691
|
-
backfillTable(db, {
|
|
14692
|
-
table: "memories",
|
|
14693
|
-
sourceKind: "memory",
|
|
14694
|
-
sourceIdColumn: "id",
|
|
14695
|
-
contentColumn: "content",
|
|
14696
|
-
scannedAt
|
|
14697
|
-
});
|
|
14698
|
-
backfillTable(db, {
|
|
14699
|
-
table: "memory_artifacts",
|
|
14700
|
-
sourceKind: "artifact",
|
|
14701
|
-
sourceIdColumn: "source_path",
|
|
14702
|
-
contentColumn: "content",
|
|
14703
|
-
scannedAt
|
|
14704
|
-
});
|
|
14705
|
-
backfillTable(db, {
|
|
14706
|
-
table: "session_transcripts",
|
|
14707
|
-
sourceKind: "transcript",
|
|
14708
|
-
sourceIdColumn: "session_key",
|
|
14709
|
-
contentColumn: "content",
|
|
14710
|
-
scannedAt
|
|
14711
|
-
});
|
|
14712
|
-
backfillTable(db, {
|
|
14713
|
-
table: "session_summaries",
|
|
14714
|
-
sourceKind: "summary",
|
|
14715
|
-
sourceIdColumn: "id",
|
|
14716
|
-
contentColumn: "content",
|
|
14717
|
-
scannedAt
|
|
14718
|
-
});
|
|
14719
|
-
backfillTable(db, {
|
|
14720
|
-
table: "embeddings",
|
|
14721
|
-
sourceKind: "source_chunk",
|
|
14722
|
-
sourceIdColumn: "id",
|
|
14723
|
-
contentColumn: "chunk_text",
|
|
14724
|
-
where: "source_type IN ('source_chunk', 'source_obsidian_chunk')",
|
|
14725
|
-
scannedAt
|
|
14726
|
-
});
|
|
14727
14562
|
}
|
|
14728
14563
|
function up131(db) {
|
|
14729
14564
|
const table = db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'dreaming_attention'").get();
|
|
@@ -14827,13 +14662,13 @@ function up133(db) {
|
|
|
14827
14662
|
AND error IS NOT NULL;
|
|
14828
14663
|
`);
|
|
14829
14664
|
}
|
|
14830
|
-
function
|
|
14665
|
+
function tableExists2(db, table) {
|
|
14831
14666
|
return db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(table) != null;
|
|
14832
14667
|
}
|
|
14833
14668
|
function up134(db) {
|
|
14834
|
-
if (!
|
|
14669
|
+
if (!tableExists2(db, "memory_jobs"))
|
|
14835
14670
|
return;
|
|
14836
|
-
if (!
|
|
14671
|
+
if (!tableExists2(db, "job_cancellations")) {
|
|
14837
14672
|
throw new Error("job_cancellations table missing; cannot retire structural jobs without an audit trail");
|
|
14838
14673
|
}
|
|
14839
14674
|
db.prepare(`
|
|
@@ -15062,12 +14897,12 @@ function up142(db) {
|
|
|
15062
14897
|
if (!cols.has(name))
|
|
15063
14898
|
db.exec(`ALTER TABLE dreaming_passes ADD COLUMN ${name} ${type}`);
|
|
15064
14899
|
}
|
|
15065
|
-
function
|
|
14900
|
+
function hasColumn24(db, table, column) {
|
|
15066
14901
|
const rows = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
15067
14902
|
return rows.some((row) => row.name === column);
|
|
15068
14903
|
}
|
|
15069
14904
|
function addColumnIfMissing28(db, table, column, definition) {
|
|
15070
|
-
if (!
|
|
14905
|
+
if (!hasColumn24(db, table, column))
|
|
15071
14906
|
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
|
|
15072
14907
|
}
|
|
15073
14908
|
function up143(db) {
|
|
@@ -15939,6 +15774,138 @@ function up162(db) {
|
|
|
15939
15774
|
END;
|
|
15940
15775
|
`);
|
|
15941
15776
|
}
|
|
15777
|
+
function up163(db) {
|
|
15778
|
+
db.exec(`
|
|
15779
|
+
DROP TRIGGER IF EXISTS memory_content_safety_head_freshness_ai;
|
|
15780
|
+
DROP TRIGGER IF EXISTS memory_content_safety_head_freshness_au;
|
|
15781
|
+
DROP INDEX IF EXISTS idx_memory_content_safety_status;
|
|
15782
|
+
DROP INDEX IF EXISTS idx_memory_content_safety_eligibility;
|
|
15783
|
+
DROP TABLE IF EXISTS memory_content_safety;
|
|
15784
|
+
`);
|
|
15785
|
+
}
|
|
15786
|
+
var COLUMNS3 = ["occurred_start", "occurred_end", "valid_from", "valid_until", "time_precision"];
|
|
15787
|
+
function up164(db) {
|
|
15788
|
+
const cols = db.prepare("PRAGMA table_info(entity_attributes)").all();
|
|
15789
|
+
for (const column of COLUMNS3) {
|
|
15790
|
+
if (!cols.some((col) => col.name === column)) {
|
|
15791
|
+
db.exec(`ALTER TABLE entity_attributes ADD COLUMN ${column} TEXT`);
|
|
15792
|
+
}
|
|
15793
|
+
}
|
|
15794
|
+
}
|
|
15795
|
+
function up165(db) {
|
|
15796
|
+
const passColumns = db.prepare("PRAGMA table_info(dreaming_passes)").all();
|
|
15797
|
+
if (!passColumns.some((column) => column.name === "scope_key")) {
|
|
15798
|
+
db.exec("ALTER TABLE dreaming_passes ADD COLUMN scope_key TEXT");
|
|
15799
|
+
}
|
|
15800
|
+
db.exec(`
|
|
15801
|
+
CREATE TABLE IF NOT EXISTS dreaming_history_nodes (
|
|
15802
|
+
agent_id TEXT NOT NULL,
|
|
15803
|
+
scope_key TEXT NOT NULL,
|
|
15804
|
+
level INTEGER NOT NULL,
|
|
15805
|
+
idx INTEGER NOT NULL,
|
|
15806
|
+
pass_id TEXT,
|
|
15807
|
+
text TEXT NOT NULL,
|
|
15808
|
+
tokens_input INTEGER,
|
|
15809
|
+
tokens_output INTEGER,
|
|
15810
|
+
tokens_cache_read INTEGER,
|
|
15811
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
15812
|
+
PRIMARY KEY (agent_id, scope_key, level, idx)
|
|
15813
|
+
);
|
|
15814
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_dreaming_history_nodes_pass
|
|
15815
|
+
ON dreaming_history_nodes(pass_id) WHERE pass_id IS NOT NULL;
|
|
15816
|
+
`);
|
|
15817
|
+
}
|
|
15818
|
+
function up166(db) {
|
|
15819
|
+
const columns = db.prepare("PRAGMA table_info(dreaming_passes)").all();
|
|
15820
|
+
if (!columns.some((column) => column.name === "tokens_peak_context")) {
|
|
15821
|
+
db.exec("ALTER TABLE dreaming_passes ADD COLUMN tokens_peak_context INTEGER");
|
|
15822
|
+
}
|
|
15823
|
+
}
|
|
15824
|
+
function up167(db) {
|
|
15825
|
+
const sql = readMemoriesFtsSql(db);
|
|
15826
|
+
if (sql !== null && !memoriesFtsNeedsTokenizerRepair(sql))
|
|
15827
|
+
return;
|
|
15828
|
+
recreateMemoriesFts(db);
|
|
15829
|
+
}
|
|
15830
|
+
function hasTable5(db, table) {
|
|
15831
|
+
return db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(table) != null;
|
|
15832
|
+
}
|
|
15833
|
+
function hasColumn25(db, table, column) {
|
|
15834
|
+
return db.prepare(`PRAGMA table_info(${table})`).all().some((row) => row.name === column);
|
|
15835
|
+
}
|
|
15836
|
+
function up168(db) {
|
|
15837
|
+
if (!hasTable5(db, "entity_attributes") || !hasTable5(db, "entity_aspects") || !hasTable5(db, "entities") || !hasColumn25(db, "entity_attributes", "source_root") || !hasColumn25(db, "entities", "source_root")) {
|
|
15838
|
+
return;
|
|
15839
|
+
}
|
|
15840
|
+
const now = new Date().toISOString();
|
|
15841
|
+
db.exec(`
|
|
15842
|
+
CREATE TEMP TABLE retired_source_paragraphs AS
|
|
15843
|
+
SELECT attr.id, attr.memory_id, attr.agent_id, asp.entity_id
|
|
15844
|
+
FROM entity_attributes attr
|
|
15845
|
+
JOIN entity_aspects asp ON asp.id = attr.aspect_id
|
|
15846
|
+
JOIN entities e ON e.id = asp.entity_id
|
|
15847
|
+
WHERE e.entity_type IN ('source_document', 'source_document_reference')
|
|
15848
|
+
AND attr.source_root IS NOT NULL
|
|
15849
|
+
AND attr.source_root = e.source_root
|
|
15850
|
+
AND attr.source_id = e.source_id
|
|
15851
|
+
AND attr.source_root NOT IN ('dreaming', 'dreaming_attention')
|
|
15852
|
+
`);
|
|
15853
|
+
if (hasColumn25(db, "entity_attributes", "memory_id") && hasTable5(db, "memories")) {
|
|
15854
|
+
if (hasTable5(db, "memory_history")) {
|
|
15855
|
+
db.prepare(`INSERT INTO memory_history (id, memory_id, event, old_content, new_content, changed_by, reason, metadata, created_at)
|
|
15856
|
+
SELECT lower(hex(randomblob(16))), mem.id, 'deleted', mem.content, NULL, 'migration:168',
|
|
15857
|
+
'Source paragraph claims retired', '{"force":true}', ?
|
|
15858
|
+
FROM memories mem
|
|
15859
|
+
JOIN retired_source_paragraphs retired ON retired.memory_id = mem.id
|
|
15860
|
+
WHERE mem.is_deleted = 0`).run(now);
|
|
15861
|
+
}
|
|
15862
|
+
if (hasTable5(db, "derived_memory_sources") && hasColumn25(db, "memories", "stale_at")) {
|
|
15863
|
+
db.prepare(`UPDATE memories
|
|
15864
|
+
SET stale_at = ?
|
|
15865
|
+
WHERE stale_at IS NULL
|
|
15866
|
+
AND is_deleted = 0
|
|
15867
|
+
AND id IN (
|
|
15868
|
+
SELECT dms.derived_memory_id
|
|
15869
|
+
FROM derived_memory_sources dms
|
|
15870
|
+
JOIN retired_source_paragraphs retired ON retired.agent_id = dms.agent_id
|
|
15871
|
+
WHERE (dms.source_kind = 'memory' AND dms.source_id = retired.memory_id)
|
|
15872
|
+
OR (dms.source_kind = 'ontology_claim' AND dms.source_id = retired.id)
|
|
15873
|
+
)`).run(now);
|
|
15874
|
+
}
|
|
15875
|
+
const forget = [
|
|
15876
|
+
"is_deleted = 1",
|
|
15877
|
+
...["deleted_at", "updated_at"].filter((column) => hasColumn25(db, "memories", column)).map((column) => `${column} = $now`),
|
|
15878
|
+
...hasColumn25(db, "memories", "updated_by") ? ["updated_by = 'migration:168'"] : [],
|
|
15879
|
+
...hasColumn25(db, "memories", "version") ? ["version = version + 1"] : []
|
|
15880
|
+
];
|
|
15881
|
+
db.prepare(`UPDATE memories
|
|
15882
|
+
SET ${forget.join(", ")}
|
|
15883
|
+
WHERE is_deleted = 0
|
|
15884
|
+
AND id IN (SELECT memory_id FROM retired_source_paragraphs WHERE memory_id IS NOT NULL)`).run({ $now: now });
|
|
15885
|
+
if (hasTable5(db, "memory_entity_mentions")) {
|
|
15886
|
+
db.exec(`
|
|
15887
|
+
DELETE FROM memory_entity_mentions
|
|
15888
|
+
WHERE memory_id IN (SELECT memory_id FROM retired_source_paragraphs WHERE memory_id IS NOT NULL)
|
|
15889
|
+
`);
|
|
15890
|
+
}
|
|
15891
|
+
}
|
|
15892
|
+
if (hasTable5(db, "ontology_contradictions")) {
|
|
15893
|
+
db.prepare(`UPDATE ontology_contradictions
|
|
15894
|
+
SET status = 'resolved', resolved_at = ?, resolution_reason = 'source paragraph claims retired', updated_at = ?
|
|
15895
|
+
WHERE status = 'active'
|
|
15896
|
+
AND (left_attribute_id IN (SELECT id FROM retired_source_paragraphs)
|
|
15897
|
+
OR right_attribute_id IN (SELECT id FROM retired_source_paragraphs))`).run(now, now);
|
|
15898
|
+
}
|
|
15899
|
+
db.exec(`
|
|
15900
|
+
DELETE FROM entity_attributes WHERE id IN (SELECT id FROM retired_source_paragraphs);
|
|
15901
|
+
DELETE FROM entity_aspects
|
|
15902
|
+
WHERE entity_id IN (
|
|
15903
|
+
SELECT id FROM entities WHERE entity_type IN ('source_document', 'source_document_reference')
|
|
15904
|
+
)
|
|
15905
|
+
AND NOT EXISTS (SELECT 1 FROM entity_attributes attr WHERE attr.aspect_id = entity_aspects.id);
|
|
15906
|
+
DROP TABLE retired_source_paragraphs;
|
|
15907
|
+
`);
|
|
15908
|
+
}
|
|
15942
15909
|
var MIGRATIONS = [
|
|
15943
15910
|
{
|
|
15944
15911
|
version: 1,
|
|
@@ -16948,8 +16915,7 @@ var MIGRATIONS = [
|
|
|
16948
16915
|
{
|
|
16949
16916
|
version: 125,
|
|
16950
16917
|
name: "memory-content-safety",
|
|
16951
|
-
up: up130
|
|
16952
|
-
artifacts: { tables: ["memory_content_safety"] }
|
|
16918
|
+
up: up130
|
|
16953
16919
|
},
|
|
16954
16920
|
{
|
|
16955
16921
|
version: 126,
|
|
@@ -17251,6 +17217,48 @@ var MIGRATIONS = [
|
|
|
17251
17217
|
name: "generic-entity-prune-scan-generation",
|
|
17252
17218
|
up: up162,
|
|
17253
17219
|
artifacts: { tables: ["generic_entity_prune_scan_state"] }
|
|
17220
|
+
},
|
|
17221
|
+
{
|
|
17222
|
+
version: 163,
|
|
17223
|
+
name: "retire-memory-content-safety",
|
|
17224
|
+
up: up163
|
|
17225
|
+
},
|
|
17226
|
+
{
|
|
17227
|
+
version: 164,
|
|
17228
|
+
name: "claim-event-time",
|
|
17229
|
+
up: up164,
|
|
17230
|
+
artifacts: {
|
|
17231
|
+
columns: ["occurred_start", "occurred_end", "valid_from", "valid_until", "time_precision"].map((column) => ({
|
|
17232
|
+
table: "entity_attributes",
|
|
17233
|
+
column
|
|
17234
|
+
}))
|
|
17235
|
+
}
|
|
17236
|
+
},
|
|
17237
|
+
{
|
|
17238
|
+
version: 165,
|
|
17239
|
+
name: "dreaming-history",
|
|
17240
|
+
up: up165,
|
|
17241
|
+
artifacts: {
|
|
17242
|
+
tables: ["dreaming_history_nodes"],
|
|
17243
|
+
indexes: ["idx_dreaming_history_nodes_pass"],
|
|
17244
|
+
columns: [{ table: "dreaming_passes", column: "scope_key" }]
|
|
17245
|
+
}
|
|
17246
|
+
},
|
|
17247
|
+
{
|
|
17248
|
+
version: 166,
|
|
17249
|
+
name: "dreaming-pass-peak-context",
|
|
17250
|
+
up: up166,
|
|
17251
|
+
artifacts: { columns: [{ table: "dreaming_passes", column: "tokens_peak_context" }] }
|
|
17252
|
+
},
|
|
17253
|
+
{
|
|
17254
|
+
version: 167,
|
|
17255
|
+
name: "memories-fts-porter",
|
|
17256
|
+
up: up167
|
|
17257
|
+
},
|
|
17258
|
+
{
|
|
17259
|
+
version: 168,
|
|
17260
|
+
name: "retire-source-paragraph-claims",
|
|
17261
|
+
up: up168
|
|
17254
17262
|
}
|
|
17255
17263
|
];
|
|
17256
17264
|
var LATEST_SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1]?.version ?? 0;
|
|
@@ -17492,6 +17500,37 @@ var PIPELINE_PROVIDER_CHOICES = [
|
|
|
17492
17500
|
var SYNTHESIS_PROVIDER_CHOICES = PIPELINE_PROVIDER_CHOICES.filter((provider) => provider !== "command");
|
|
17493
17501
|
var PIPELINE_PROVIDER_SET = new Set(PIPELINE_PROVIDER_CHOICES);
|
|
17494
17502
|
var SYNTHESIS_PROVIDER_SET = new Set(SYNTHESIS_PROVIDER_CHOICES);
|
|
17503
|
+
var SECRET_NAMES = "(?:access[_-]?token|api[_-]?key|apikey|auth[_-]?token|client[_-]?secret|passw(?:or)?d|refresh[_-]?token|private[_-]?key|secret(?:[_-]?key)?|token)";
|
|
17504
|
+
var SECRET_VALUE = `(?=[^\\s"',;&<>()\\[\\]{}$]*\\d)[^\\s"',;&<>()\\[\\]{}$]{8,}`;
|
|
17505
|
+
var PATTERNS = [
|
|
17506
|
+
{
|
|
17507
|
+
kind: "private_key",
|
|
17508
|
+
pattern: /-----BEGIN (?:[A-Z]+ )*PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z]+ )*PRIVATE KEY-----/g
|
|
17509
|
+
},
|
|
17510
|
+
{
|
|
17511
|
+
kind: "provider_key",
|
|
17512
|
+
pattern: /\b(?:(?:AKIA|ASIA)[0-9A-Z]{16}|github_pat_[A-Za-z0-9_]{20,}|gh[pousr]_[A-Za-z0-9]{20,}|sk-(?:ant-|proj-)?[A-Za-z0-9_-]{20,}|(?:sk|rk)_(?:live|test)_[0-9A-Za-z]{16,}|xox[abprs]-[A-Za-z0-9-]{10,}|AIza[0-9A-Za-z_-]{35}|glpat-[A-Za-z0-9_-]{20,}|npm_[A-Za-z0-9]{36})\b/g
|
|
17513
|
+
},
|
|
17514
|
+
{
|
|
17515
|
+
kind: "jwt",
|
|
17516
|
+
pattern: /\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g
|
|
17517
|
+
},
|
|
17518
|
+
{
|
|
17519
|
+
kind: "bearer_token",
|
|
17520
|
+
pattern: /\bBearer\s+([A-Za-z0-9._~+/-]{16,}=*)/g,
|
|
17521
|
+
valueGroup: 1
|
|
17522
|
+
},
|
|
17523
|
+
{
|
|
17524
|
+
kind: "secret_assignment",
|
|
17525
|
+
pattern: new RegExp(`\\b${SECRET_NAMES}["']?\\s*[:=]\\s*["']?(${SECRET_VALUE})`, "gi"),
|
|
17526
|
+
valueGroup: 1
|
|
17527
|
+
},
|
|
17528
|
+
{
|
|
17529
|
+
kind: "secret_assignment",
|
|
17530
|
+
pattern: new RegExp(`\\b[A-Z][A-Z0-9_]*(?:API_KEY|TOKEN|SECRET|PASSWORD)\\s*=\\s*["']?(${SECRET_VALUE})`, "g"),
|
|
17531
|
+
valueGroup: 1
|
|
17532
|
+
}
|
|
17533
|
+
];
|
|
17495
17534
|
var LOOPBACK_HOST = "127.0.0.1";
|
|
17496
17535
|
function normalizeLoopbackHost(host) {
|
|
17497
17536
|
const normalized = host.trim().toLowerCase().replace(/^\[|\]$/g, "");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signetai/connector-base",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.237.0",
|
|
4
4
|
"description": "Base class for Signet harness connectors",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"typecheck": "tsc --noEmit"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@signetai/core": "0.
|
|
33
|
+
"@signetai/core": "0.237.0",
|
|
34
34
|
"json5": "^2.2.3",
|
|
35
35
|
"jsonc-parser": "3.3.1"
|
|
36
36
|
},
|