kiro-memory 2.1.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/package.json +3 -3
- package/plugin/dist/cli/contextkit.js +2315 -180
- package/plugin/dist/hooks/agentSpawn.js +548 -49
- package/plugin/dist/hooks/kiro-hooks.js +548 -49
- package/plugin/dist/hooks/postToolUse.js +556 -56
- package/plugin/dist/hooks/stop.js +548 -49
- package/plugin/dist/hooks/userPromptSubmit.js +551 -50
- package/plugin/dist/index.js +549 -50
- package/plugin/dist/plugins/github/github-client.js +152 -0
- package/plugin/dist/plugins/github/index.js +412 -0
- package/plugin/dist/plugins/github/issue-parser.js +54 -0
- package/plugin/dist/plugins/slack/formatter.js +90 -0
- package/plugin/dist/plugins/slack/index.js +215 -0
- package/plugin/dist/sdk/index.js +548 -49
- package/plugin/dist/servers/mcp-server.js +4461 -397
- package/plugin/dist/services/search/EmbeddingService.js +64 -20
- package/plugin/dist/services/search/HybridSearch.js +380 -38
- package/plugin/dist/services/search/VectorSearch.js +65 -21
- package/plugin/dist/services/search/index.js +380 -38
- package/plugin/dist/services/sqlite/Backup.js +416 -0
- package/plugin/dist/services/sqlite/Database.js +71 -0
- package/plugin/dist/services/sqlite/ImportExport.js +452 -0
- package/plugin/dist/services/sqlite/Observations.js +291 -7
- package/plugin/dist/services/sqlite/Prompts.js +1 -1
- package/plugin/dist/services/sqlite/Search.js +10 -10
- package/plugin/dist/services/sqlite/Summaries.js +4 -4
- package/plugin/dist/services/sqlite/index.js +1323 -28
- package/plugin/dist/viewer.css +1 -1
- package/plugin/dist/viewer.js +16 -8
- package/plugin/dist/viewer.js.map +4 -4
- package/plugin/dist/worker-service.js +326 -75
- package/plugin/dist/worker-service.js.map +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../node_modules/ip-address/src/common.ts", "../../node_modules/ip-address/src/v4/constants.ts", "../../node_modules/ip-address/src/address-error.ts", "../../node_modules/ip-address/src/ipv4.ts", "../../node_modules/ip-address/src/v6/constants.ts", "../../node_modules/ip-address/src/v6/helpers.ts", "../../node_modules/ip-address/src/v6/regular-expressions.ts", "../../node_modules/ip-address/src/ipv6.ts", "../../node_modules/ip-address/src/ip-address.ts", "../../src/services/sqlite/Search.ts", "../../src/services/sqlite/Observations.ts", "../../src/services/worker-service.ts", "../../node_modules/helmet/index.mjs", "../../node_modules/express-rate-limit/dist/index.mjs", "../../src/shims/bun-sqlite.ts", "../../src/shared/paths.ts", "../../src/utils/logger.ts", "../../src/services/sqlite/Database.ts", "../../src/services/search/EmbeddingService.ts", "../../src/services/search/VectorSearch.ts", "../../src/services/search/ScoringEngine.ts", "../../src/services/search/HybridSearch.ts", "../../src/services/worker-context.ts", "../../src/services/routes/core.ts", "../../src/services/
|
|
4
|
-
"sourcesContent": ["import { Address4 } from './ipv4';\nimport { Address6 } from './ipv6';\n\nexport interface ReverseFormOptions {\n omitSuffix?: boolean;\n}\n\nexport function isInSubnet(this: Address4 | Address6, address: Address4 | Address6) {\n if (this.subnetMask < address.subnetMask) {\n return false;\n }\n\n if (this.mask(address.subnetMask) === address.mask()) {\n return true;\n }\n\n return false;\n}\n\nexport function isCorrect(defaultBits: number) {\n return function (this: Address4 | Address6) {\n if (this.addressMinusSuffix !== this.correctForm()) {\n return false;\n }\n\n if (this.subnetMask === defaultBits && !this.parsedSubnet) {\n return true;\n }\n\n return this.parsedSubnet === String(this.subnetMask);\n };\n}\n\nexport function numberToPaddedHex(number: number) {\n return number.toString(16).padStart(2, '0');\n}\n\nexport function stringToPaddedHex(numberString: string) {\n return numberToPaddedHex(parseInt(numberString, 10));\n}\n\n/**\n * @param binaryValue Binary representation of a value (e.g. `10`)\n * @param position Byte position, where 0 is the least significant bit\n */\nexport function testBit(binaryValue: string, position: number): boolean {\n const { length } = binaryValue;\n\n if (position > length) {\n return false;\n }\n\n const positionInString = length - position;\n return binaryValue.substring(positionInString, positionInString + 1) === '1';\n}\n", "export const BITS = 32;\nexport const GROUPS = 4;\n\nexport const RE_ADDRESS =\n /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/g;\n\nexport const RE_SUBNET_STRING = /\\/\\d{1,2}$/;\n", "export class AddressError extends Error {\n parseMessage?: string;\n\n constructor(message: string, parseMessage?: string) {\n super(message);\n\n this.name = 'AddressError';\n\n this.parseMessage = parseMessage;\n }\n}\n", "/* eslint-disable no-param-reassign */\n\nimport * as common from './common';\nimport * as constants from './v4/constants';\nimport { AddressError } from './address-error';\n\n/**\n * Represents an IPv4 address\n * @class Address4\n * @param {string} address - An IPv4 address string\n */\nexport class Address4 {\n address: string;\n addressMinusSuffix?: string;\n groups: number = constants.GROUPS;\n parsedAddress: string[] = [];\n parsedSubnet: string = '';\n subnet: string = '/32';\n subnetMask: number = 32;\n v4: boolean = true;\n\n constructor(address: string) {\n this.address = address;\n\n const subnet = constants.RE_SUBNET_STRING.exec(address);\n\n if (subnet) {\n this.parsedSubnet = subnet[0].replace('/', '');\n this.subnetMask = parseInt(this.parsedSubnet, 10);\n this.subnet = `/${this.subnetMask}`;\n\n if (this.subnetMask < 0 || this.subnetMask > constants.BITS) {\n throw new AddressError('Invalid subnet mask.');\n }\n\n address = address.replace(constants.RE_SUBNET_STRING, '');\n }\n\n this.addressMinusSuffix = address;\n\n this.parsedAddress = this.parse(address);\n }\n\n static isValid(address: string): boolean {\n try {\n // eslint-disable-next-line no-new\n new Address4(address);\n\n return true;\n } catch (e) {\n return false;\n }\n }\n\n /*\n * Parses a v4 address\n */\n parse(address: string) {\n const groups = address.split('.');\n\n if (!address.match(constants.RE_ADDRESS)) {\n throw new AddressError('Invalid IPv4 address.');\n }\n\n return groups;\n }\n\n /**\n * Returns the correct form of an address\n * @memberof Address4\n * @instance\n * @returns {String}\n */\n correctForm(): string {\n return this.parsedAddress.map((part) => parseInt(part, 10)).join('.');\n }\n\n /**\n * Returns true if the address is correct, false otherwise\n * @memberof Address4\n * @instance\n * @returns {Boolean}\n */\n isCorrect = common.isCorrect(constants.BITS);\n\n /**\n * Converts a hex string to an IPv4 address object\n * @memberof Address4\n * @static\n * @param {string} hex - a hex string to convert\n * @returns {Address4}\n */\n static fromHex(hex: string): Address4 {\n const padded = hex.replace(/:/g, '').padStart(8, '0');\n const groups = [];\n let i;\n\n for (i = 0; i < 8; i += 2) {\n const h = padded.slice(i, i + 2);\n\n groups.push(parseInt(h, 16));\n }\n\n return new Address4(groups.join('.'));\n }\n\n /**\n * Converts an integer into a IPv4 address object\n * @memberof Address4\n * @static\n * @param {integer} integer - a number to convert\n * @returns {Address4}\n */\n static fromInteger(integer: number): Address4 {\n return Address4.fromHex(integer.toString(16));\n }\n\n /**\n * Return an address from in-addr.arpa form\n * @memberof Address4\n * @static\n * @param {string} arpaFormAddress - an 'in-addr.arpa' form ipv4 address\n * @returns {Adress4}\n * @example\n * var address = Address4.fromArpa(42.2.0.192.in-addr.arpa.)\n * address.correctForm(); // '192.0.2.42'\n */\n static fromArpa(arpaFormAddress: string): Address4 {\n // remove ending \".in-addr.arpa.\" or just \".\"\n const leader = arpaFormAddress.replace(/(\\.in-addr\\.arpa)?\\.$/, '');\n\n const address = leader.split('.').reverse().join('.');\n\n return new Address4(address);\n }\n\n /**\n * Converts an IPv4 address object to a hex string\n * @memberof Address4\n * @instance\n * @returns {String}\n */\n toHex(): string {\n return this.parsedAddress.map((part) => common.stringToPaddedHex(part)).join(':');\n }\n\n /**\n * Converts an IPv4 address object to an array of bytes\n * @memberof Address4\n * @instance\n * @returns {Array}\n */\n toArray(): number[] {\n return this.parsedAddress.map((part) => parseInt(part, 10));\n }\n\n /**\n * Converts an IPv4 address object to an IPv6 address group\n * @memberof Address4\n * @instance\n * @returns {String}\n */\n toGroup6(): string {\n const output = [];\n let i;\n\n for (i = 0; i < constants.GROUPS; i += 2) {\n output.push(\n `${common.stringToPaddedHex(this.parsedAddress[i])}${common.stringToPaddedHex(\n this.parsedAddress[i + 1],\n )}`,\n );\n }\n\n return output.join(':');\n }\n\n /**\n * Returns the address as a `bigint`\n * @memberof Address4\n * @instance\n * @returns {bigint}\n */\n bigInt(): bigint {\n return BigInt(`0x${this.parsedAddress.map((n) => common.stringToPaddedHex(n)).join('')}`);\n }\n\n /**\n * Helper function getting start address.\n * @memberof Address4\n * @instance\n * @returns {bigint}\n */\n _startAddress(): bigint {\n return BigInt(`0b${this.mask() + '0'.repeat(constants.BITS - this.subnetMask)}`);\n }\n\n /**\n * The first address in the range given by this address' subnet.\n * Often referred to as the Network Address.\n * @memberof Address4\n * @instance\n * @returns {Address4}\n */\n startAddress(): Address4 {\n return Address4.fromBigInt(this._startAddress());\n }\n\n /**\n * The first host address in the range given by this address's subnet ie\n * the first address after the Network Address\n * @memberof Address4\n * @instance\n * @returns {Address4}\n */\n startAddressExclusive(): Address4 {\n const adjust = BigInt('1');\n return Address4.fromBigInt(this._startAddress() + adjust);\n }\n\n /**\n * Helper function getting end address.\n * @memberof Address4\n * @instance\n * @returns {bigint}\n */\n _endAddress(): bigint {\n return BigInt(`0b${this.mask() + '1'.repeat(constants.BITS - this.subnetMask)}`);\n }\n\n /**\n * The last address in the range given by this address' subnet\n * Often referred to as the Broadcast\n * @memberof Address4\n * @instance\n * @returns {Address4}\n */\n endAddress(): Address4 {\n return Address4.fromBigInt(this._endAddress());\n }\n\n /**\n * The last host address in the range given by this address's subnet ie\n * the last address prior to the Broadcast Address\n * @memberof Address4\n * @instance\n * @returns {Address4}\n */\n endAddressExclusive(): Address4 {\n const adjust = BigInt('1');\n return Address4.fromBigInt(this._endAddress() - adjust);\n }\n\n /**\n * Converts a BigInt to a v4 address object\n * @memberof Address4\n * @static\n * @param {bigint} bigInt - a BigInt to convert\n * @returns {Address4}\n */\n static fromBigInt(bigInt: bigint): Address4 {\n return Address4.fromHex(bigInt.toString(16));\n }\n\n /**\n * Returns the first n bits of the address, defaulting to the\n * subnet mask\n * @memberof Address4\n * @instance\n * @returns {String}\n */\n mask(mask?: number): string {\n if (mask === undefined) {\n mask = this.subnetMask;\n }\n\n return this.getBitsBase2(0, mask);\n }\n\n /**\n * Returns the bits in the given range as a base-2 string\n * @memberof Address4\n * @instance\n * @returns {string}\n */\n getBitsBase2(start: number, end: number): string {\n return this.binaryZeroPad().slice(start, end);\n }\n\n /**\n * Return the reversed ip6.arpa form of the address\n * @memberof Address4\n * @param {Object} options\n * @param {boolean} options.omitSuffix - omit the \"in-addr.arpa\" suffix\n * @instance\n * @returns {String}\n */\n reverseForm(options?: common.ReverseFormOptions): string {\n if (!options) {\n options = {};\n }\n\n const reversed = this.correctForm().split('.').reverse().join('.');\n\n if (options.omitSuffix) {\n return reversed;\n }\n\n return `${reversed}.in-addr.arpa.`;\n }\n\n /**\n * Returns true if the given address is in the subnet of the current address\n * @memberof Address4\n * @instance\n * @returns {boolean}\n */\n isInSubnet = common.isInSubnet;\n\n /**\n * Returns true if the given address is a multicast address\n * @memberof Address4\n * @instance\n * @returns {boolean}\n */\n isMulticast(): boolean {\n return this.isInSubnet(new Address4('224.0.0.0/4'));\n }\n\n /**\n * Returns a zero-padded base-2 string representation of the address\n * @memberof Address4\n * @instance\n * @returns {string}\n */\n binaryZeroPad(): string {\n return this.bigInt().toString(2).padStart(constants.BITS, '0');\n }\n\n /**\n * Groups an IPv4 address for inclusion at the end of an IPv6 address\n * @returns {String}\n */\n groupForV6(): string {\n const segments = this.parsedAddress;\n\n return this.address.replace(\n constants.RE_ADDRESS,\n `<span class=\"hover-group group-v4 group-6\">${segments\n .slice(0, 2)\n .join('.')}</span>.<span class=\"hover-group group-v4 group-7\">${segments\n .slice(2, 4)\n .join('.')}</span>`,\n );\n }\n}\n", "export const BITS = 128;\nexport const GROUPS = 8;\n\n/**\n * Represents IPv6 address scopes\n * @memberof Address6\n * @static\n */\nexport const SCOPES: { [key: number]: string | undefined } = {\n 0: 'Reserved',\n 1: 'Interface local',\n 2: 'Link local',\n 4: 'Admin local',\n 5: 'Site local',\n 8: 'Organization local',\n 14: 'Global',\n 15: 'Reserved',\n} as const;\n\n/**\n * Represents IPv6 address types\n * @memberof Address6\n * @static\n */\nexport const TYPES: { [key: string]: string | undefined } = {\n 'ff01::1/128': 'Multicast (All nodes on this interface)',\n 'ff01::2/128': 'Multicast (All routers on this interface)',\n 'ff02::1/128': 'Multicast (All nodes on this link)',\n 'ff02::2/128': 'Multicast (All routers on this link)',\n 'ff05::2/128': 'Multicast (All routers in this site)',\n 'ff02::5/128': 'Multicast (OSPFv3 AllSPF routers)',\n 'ff02::6/128': 'Multicast (OSPFv3 AllDR routers)',\n 'ff02::9/128': 'Multicast (RIP routers)',\n 'ff02::a/128': 'Multicast (EIGRP routers)',\n 'ff02::d/128': 'Multicast (PIM routers)',\n 'ff02::16/128': 'Multicast (MLDv2 reports)',\n 'ff01::fb/128': 'Multicast (mDNSv6)',\n 'ff02::fb/128': 'Multicast (mDNSv6)',\n 'ff05::fb/128': 'Multicast (mDNSv6)',\n 'ff02::1:2/128': 'Multicast (All DHCP servers and relay agents on this link)',\n 'ff05::1:2/128': 'Multicast (All DHCP servers and relay agents in this site)',\n 'ff02::1:3/128': 'Multicast (All DHCP servers on this link)',\n 'ff05::1:3/128': 'Multicast (All DHCP servers in this site)',\n '::/128': 'Unspecified',\n '::1/128': 'Loopback',\n 'ff00::/8': 'Multicast',\n 'fe80::/10': 'Link-local unicast',\n} as const;\n\n/**\n * A regular expression that matches bad characters in an IPv6 address\n * @memberof Address6\n * @static\n */\nexport const RE_BAD_CHARACTERS = /([^0-9a-f:/%])/gi;\n\n/**\n * A regular expression that matches an incorrect IPv6 address\n * @memberof Address6\n * @static\n */\nexport const RE_BAD_ADDRESS = /([0-9a-f]{5,}|:{3,}|[^:]:$|^:[^:]|\\/$)/gi;\n\n/**\n * A regular expression that matches an IPv6 subnet\n * @memberof Address6\n * @static\n */\nexport const RE_SUBNET_STRING = /\\/\\d{1,3}(?=%|$)/;\n\n/**\n * A regular expression that matches an IPv6 zone\n * @memberof Address6\n * @static\n */\nexport const RE_ZONE_STRING = /%.*$/;\n\nexport const RE_URL = /^\\[{0,1}([0-9a-f:]+)\\]{0,1}/;\nexport const RE_URL_WITH_PORT = /\\[([0-9a-f:]+)\\]:([0-9]{1,5})/;\n", "/**\n * @returns {String} the string with all zeroes contained in a <span>\n */\nexport function spanAllZeroes(s: string): string {\n return s.replace(/(0+)/g, '<span class=\"zero\">$1</span>');\n}\n\n/**\n * @returns {String} the string with each character contained in a <span>\n */\nexport function spanAll(s: string, offset: number = 0): string {\n const letters = s.split('');\n\n return letters\n .map(\n (n, i) => `<span class=\"digit value-${n} position-${i + offset}\">${spanAllZeroes(n)}</span>`,\n )\n .join('');\n}\n\nfunction spanLeadingZeroesSimple(group: string): string {\n return group.replace(/^(0+)/, '<span class=\"zero\">$1</span>');\n}\n\n/**\n * @returns {String} the string with leading zeroes contained in a <span>\n */\nexport function spanLeadingZeroes(address: string): string {\n const groups = address.split(':');\n\n return groups.map((g) => spanLeadingZeroesSimple(g)).join(':');\n}\n\n/**\n * Groups an address\n * @returns {String} a grouped address\n */\nexport function simpleGroup(addressString: string, offset: number = 0): string[] {\n const groups = addressString.split(':');\n\n return groups.map((g, i) => {\n if (/group-v4/.test(g)) {\n return g;\n }\n\n return `<span class=\"hover-group group-${i + offset}\">${spanLeadingZeroesSimple(g)}</span>`;\n });\n}\n", "import * as v6 from './constants';\n\nexport function groupPossibilities(possibilities: string[]): string {\n return `(${possibilities.join('|')})`;\n}\n\nexport function padGroup(group: string): string {\n if (group.length < 4) {\n return `0{0,${4 - group.length}}${group}`;\n }\n\n return group;\n}\n\nexport const ADDRESS_BOUNDARY = '[^A-Fa-f0-9:]';\n\nexport function simpleRegularExpression(groups: string[]) {\n const zeroIndexes: number[] = [];\n\n groups.forEach((group, i) => {\n const groupInteger = parseInt(group, 16);\n\n if (groupInteger === 0) {\n zeroIndexes.push(i);\n }\n });\n\n // You can technically elide a single 0, this creates the regular expressions\n // to match that eventuality\n const possibilities = zeroIndexes.map((zeroIndex) =>\n groups\n .map((group, i) => {\n if (i === zeroIndex) {\n const elision = i === 0 || i === v6.GROUPS - 1 ? ':' : '';\n\n return groupPossibilities([padGroup(group), elision]);\n }\n\n return padGroup(group);\n })\n .join(':'),\n );\n\n // The simplest case\n possibilities.push(groups.map(padGroup).join(':'));\n\n return groupPossibilities(possibilities);\n}\n\nexport function possibleElisions(\n elidedGroups: number,\n moreLeft?: boolean,\n moreRight?: boolean,\n): string {\n const left = moreLeft ? '' : ':';\n const right = moreRight ? '' : ':';\n\n const possibilities = [];\n\n // 1. elision of everything (::)\n if (!moreLeft && !moreRight) {\n possibilities.push('::');\n }\n\n // 2. complete elision of the middle\n if (moreLeft && moreRight) {\n possibilities.push('');\n }\n\n if ((moreRight && !moreLeft) || (!moreRight && moreLeft)) {\n // 3. complete elision of one side\n possibilities.push(':');\n }\n\n // 4. elision from the left side\n possibilities.push(`${left}(:0{1,4}){1,${elidedGroups - 1}}`);\n\n // 5. elision from the right side\n possibilities.push(`(0{1,4}:){1,${elidedGroups - 1}}${right}`);\n\n // 6. no elision\n possibilities.push(`(0{1,4}:){${elidedGroups - 1}}0{1,4}`);\n\n // 7. elision (including sloppy elision) from the middle\n for (let groups = 1; groups < elidedGroups - 1; groups++) {\n for (let position = 1; position < elidedGroups - groups; position++) {\n possibilities.push(\n `(0{1,4}:){${position}}:(0{1,4}:){${elidedGroups - position - groups - 1}}0{1,4}`,\n );\n }\n }\n\n return groupPossibilities(possibilities);\n}\n", "/* eslint-disable prefer-destructuring */\n/* eslint-disable no-param-reassign */\n\nimport * as common from './common';\nimport * as constants4 from './v4/constants';\nimport * as constants6 from './v6/constants';\nimport * as helpers from './v6/helpers';\nimport { Address4 } from './ipv4';\nimport {\n ADDRESS_BOUNDARY,\n possibleElisions,\n simpleRegularExpression,\n} from './v6/regular-expressions';\nimport { AddressError } from './address-error';\nimport { testBit } from './common';\n\nfunction assert(condition: any): asserts condition {\n if (!condition) {\n throw new Error('Assertion failed.');\n }\n}\n\nfunction addCommas(number: string): string {\n const r = /(\\d+)(\\d{3})/;\n\n while (r.test(number)) {\n number = number.replace(r, '$1,$2');\n }\n\n return number;\n}\n\nfunction spanLeadingZeroes4(n: string): string {\n n = n.replace(/^(0{1,})([1-9]+)$/, '<span class=\"parse-error\">$1</span>$2');\n n = n.replace(/^(0{1,})(0)$/, '<span class=\"parse-error\">$1</span>$2');\n\n return n;\n}\n\n/*\n * A helper function to compact an array\n */\nfunction compact(address: string[], slice: number[]) {\n const s1 = [];\n const s2 = [];\n let i;\n\n for (i = 0; i < address.length; i++) {\n if (i < slice[0]) {\n s1.push(address[i]);\n } else if (i > slice[1]) {\n s2.push(address[i]);\n }\n }\n\n return s1.concat(['compact']).concat(s2);\n}\n\nfunction paddedHex(octet: string): string {\n return parseInt(octet, 16).toString(16).padStart(4, '0');\n}\n\nfunction unsignByte(b: number) {\n // eslint-disable-next-line no-bitwise\n return b & 0xff;\n}\n\ninterface SixToFourProperties {\n prefix: string;\n gateway: string;\n}\n\ninterface TeredoProperties {\n prefix: string;\n server4: string;\n client4: string;\n flags: string;\n coneNat: boolean;\n microsoft: {\n reserved: boolean;\n universalLocal: boolean;\n groupIndividual: boolean;\n nonce: string;\n };\n udpPort: string;\n}\n\n/**\n * Represents an IPv6 address\n * @class Address6\n * @param {string} address - An IPv6 address string\n * @param {number} [groups=8] - How many octets to parse\n * @example\n * var address = new Address6('2001::/32');\n */\nexport class Address6 {\n address4?: Address4;\n address: string;\n addressMinusSuffix: string = '';\n elidedGroups?: number;\n elisionBegin?: number;\n elisionEnd?: number;\n groups: number;\n parsedAddress4?: string;\n parsedAddress: string[];\n parsedSubnet: string = '';\n subnet: string = '/128';\n subnetMask: number = 128;\n v4: boolean = false;\n zone: string = '';\n\n constructor(address: string, optionalGroups?: number) {\n if (optionalGroups === undefined) {\n this.groups = constants6.GROUPS;\n } else {\n this.groups = optionalGroups;\n }\n\n this.address = address;\n\n const subnet = constants6.RE_SUBNET_STRING.exec(address);\n\n if (subnet) {\n this.parsedSubnet = subnet[0].replace('/', '');\n this.subnetMask = parseInt(this.parsedSubnet, 10);\n this.subnet = `/${this.subnetMask}`;\n\n if (\n Number.isNaN(this.subnetMask) ||\n this.subnetMask < 0 ||\n this.subnetMask > constants6.BITS\n ) {\n throw new AddressError('Invalid subnet mask.');\n }\n\n address = address.replace(constants6.RE_SUBNET_STRING, '');\n } else if (/\\//.test(address)) {\n throw new AddressError('Invalid subnet mask.');\n }\n\n const zone = constants6.RE_ZONE_STRING.exec(address);\n\n if (zone) {\n this.zone = zone[0];\n\n address = address.replace(constants6.RE_ZONE_STRING, '');\n }\n\n this.addressMinusSuffix = address;\n\n this.parsedAddress = this.parse(this.addressMinusSuffix);\n }\n\n static isValid(address: string): boolean {\n try {\n // eslint-disable-next-line no-new\n new Address6(address);\n\n return true;\n } catch (e) {\n return false;\n }\n }\n\n /**\n * Convert a BigInt to a v6 address object\n * @memberof Address6\n * @static\n * @param {bigint} bigInt - a BigInt to convert\n * @returns {Address6}\n * @example\n * var bigInt = BigInt('1000000000000');\n * var address = Address6.fromBigInt(bigInt);\n * address.correctForm(); // '::e8:d4a5:1000'\n */\n static fromBigInt(bigInt: bigint): Address6 {\n const hex = bigInt.toString(16).padStart(32, '0');\n const groups = [];\n let i;\n\n for (i = 0; i < constants6.GROUPS; i++) {\n groups.push(hex.slice(i * 4, (i + 1) * 4));\n }\n\n return new Address6(groups.join(':'));\n }\n\n /**\n * Convert a URL (with optional port number) to an address object\n * @memberof Address6\n * @static\n * @param {string} url - a URL with optional port number\n * @example\n * var addressAndPort = Address6.fromURL('http://[ffff::]:8080/foo/');\n * addressAndPort.address.correctForm(); // 'ffff::'\n * addressAndPort.port; // 8080\n */\n static fromURL(url: string) {\n let host: string;\n let port: string | number | null = null;\n let result: string[] | null;\n\n // If we have brackets parse them and find a port\n if (url.indexOf('[') !== -1 && url.indexOf(']:') !== -1) {\n result = constants6.RE_URL_WITH_PORT.exec(url);\n\n if (result === null) {\n return {\n error: 'failed to parse address with port',\n address: null,\n port: null,\n };\n }\n\n host = result[1];\n port = result[2];\n // If there's a URL extract the address\n } else if (url.indexOf('/') !== -1) {\n // Remove the protocol prefix\n url = url.replace(/^[a-z0-9]+:\\/\\//, '');\n\n // Parse the address\n result = constants6.RE_URL.exec(url);\n\n if (result === null) {\n return {\n error: 'failed to parse address from URL',\n address: null,\n port: null,\n };\n }\n\n host = result[1];\n // Otherwise just assign the URL to the host and let the library parse it\n } else {\n host = url;\n }\n\n // If there's a port convert it to an integer\n if (port) {\n port = parseInt(port, 10);\n\n // squelch out of range ports\n if (port < 0 || port > 65536) {\n port = null;\n }\n } else {\n // Standardize `undefined` to `null`\n port = null;\n }\n\n return {\n address: new Address6(host),\n port,\n };\n }\n\n /**\n * Create an IPv6-mapped address given an IPv4 address\n * @memberof Address6\n * @static\n * @param {string} address - An IPv4 address string\n * @returns {Address6}\n * @example\n * var address = Address6.fromAddress4('192.168.0.1');\n * address.correctForm(); // '::ffff:c0a8:1'\n * address.to4in6(); // '::ffff:192.168.0.1'\n */\n static fromAddress4(address: string): Address6 {\n const address4 = new Address4(address);\n\n const mask6 = constants6.BITS - (constants4.BITS - address4.subnetMask);\n\n return new Address6(`::ffff:${address4.correctForm()}/${mask6}`);\n }\n\n /**\n * Return an address from ip6.arpa form\n * @memberof Address6\n * @static\n * @param {string} arpaFormAddress - an 'ip6.arpa' form address\n * @returns {Adress6}\n * @example\n * var address = Address6.fromArpa(e.f.f.f.3.c.2.6.f.f.f.e.6.6.8.e.1.0.6.7.9.4.e.c.0.0.0.0.1.0.0.2.ip6.arpa.)\n * address.correctForm(); // '2001:0:ce49:7601:e866:efff:62c3:fffe'\n */\n static fromArpa(arpaFormAddress: string): Address6 {\n // remove ending \".ip6.arpa.\" or just \".\"\n let address = arpaFormAddress.replace(/(\\.ip6\\.arpa)?\\.$/, '');\n const semicolonAmount = 7;\n\n // correct ip6.arpa form with ending removed will be 63 characters\n if (address.length !== 63) {\n throw new AddressError(\"Invalid 'ip6.arpa' form.\");\n }\n\n const parts = address.split('.').reverse();\n\n for (let i = semicolonAmount; i > 0; i--) {\n const insertIndex = i * 4;\n parts.splice(insertIndex, 0, ':');\n }\n\n address = parts.join('');\n\n return new Address6(address);\n }\n\n /**\n * Return the Microsoft UNC transcription of the address\n * @memberof Address6\n * @instance\n * @returns {String} the Microsoft UNC transcription of the address\n */\n microsoftTranscription(): string {\n return `${this.correctForm().replace(/:/g, '-')}.ipv6-literal.net`;\n }\n\n /**\n * Return the first n bits of the address, defaulting to the subnet mask\n * @memberof Address6\n * @instance\n * @param {number} [mask=subnet] - the number of bits to mask\n * @returns {String} the first n bits of the address as a string\n */\n mask(mask: number = this.subnetMask): string {\n return this.getBitsBase2(0, mask);\n }\n\n /**\n * Return the number of possible subnets of a given size in the address\n * @memberof Address6\n * @instance\n * @param {number} [subnetSize=128] - the subnet size\n * @returns {String}\n */\n // TODO: probably useful to have a numeric version of this too\n possibleSubnets(subnetSize: number = 128): string {\n const availableBits = constants6.BITS - this.subnetMask;\n const subnetBits = Math.abs(subnetSize - constants6.BITS);\n const subnetPowers = availableBits - subnetBits;\n\n if (subnetPowers < 0) {\n return '0';\n }\n\n return addCommas((BigInt('2') ** BigInt(subnetPowers)).toString(10));\n }\n\n /**\n * Helper function getting start address.\n * @memberof Address6\n * @instance\n * @returns {bigint}\n */\n _startAddress(): bigint {\n return BigInt(`0b${this.mask() + '0'.repeat(constants6.BITS - this.subnetMask)}`);\n }\n\n /**\n * The first address in the range given by this address' subnet\n * Often referred to as the Network Address.\n * @memberof Address6\n * @instance\n * @returns {Address6}\n */\n startAddress(): Address6 {\n return Address6.fromBigInt(this._startAddress());\n }\n\n /**\n * The first host address in the range given by this address's subnet ie\n * the first address after the Network Address\n * @memberof Address6\n * @instance\n * @returns {Address6}\n */\n startAddressExclusive(): Address6 {\n const adjust = BigInt('1');\n return Address6.fromBigInt(this._startAddress() + adjust);\n }\n\n /**\n * Helper function getting end address.\n * @memberof Address6\n * @instance\n * @returns {bigint}\n */\n _endAddress(): bigint {\n return BigInt(`0b${this.mask() + '1'.repeat(constants6.BITS - this.subnetMask)}`);\n }\n\n /**\n * The last address in the range given by this address' subnet\n * Often referred to as the Broadcast\n * @memberof Address6\n * @instance\n * @returns {Address6}\n */\n endAddress(): Address6 {\n return Address6.fromBigInt(this._endAddress());\n }\n\n /**\n * The last host address in the range given by this address's subnet ie\n * the last address prior to the Broadcast Address\n * @memberof Address6\n * @instance\n * @returns {Address6}\n */\n endAddressExclusive(): Address6 {\n const adjust = BigInt('1');\n return Address6.fromBigInt(this._endAddress() - adjust);\n }\n\n /**\n * Return the scope of the address\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n getScope(): string {\n let scope = constants6.SCOPES[parseInt(this.getBits(12, 16).toString(10), 10)];\n\n if (this.getType() === 'Global unicast' && scope !== 'Link local') {\n scope = 'Global';\n }\n\n return scope || 'Unknown';\n }\n\n /**\n * Return the type of the address\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n getType(): string {\n for (const subnet of Object.keys(constants6.TYPES)) {\n if (this.isInSubnet(new Address6(subnet))) {\n return constants6.TYPES[subnet] as string;\n }\n }\n\n return 'Global unicast';\n }\n\n /**\n * Return the bits in the given range as a BigInt\n * @memberof Address6\n * @instance\n * @returns {bigint}\n */\n getBits(start: number, end: number): bigint {\n return BigInt(`0b${this.getBitsBase2(start, end)}`);\n }\n\n /**\n * Return the bits in the given range as a base-2 string\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n getBitsBase2(start: number, end: number): string {\n return this.binaryZeroPad().slice(start, end);\n }\n\n /**\n * Return the bits in the given range as a base-16 string\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n getBitsBase16(start: number, end: number): string {\n const length = end - start;\n\n if (length % 4 !== 0) {\n throw new Error('Length of bits to retrieve must be divisible by four');\n }\n\n return this.getBits(start, end)\n .toString(16)\n .padStart(length / 4, '0');\n }\n\n /**\n * Return the bits that are set past the subnet mask length\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n getBitsPastSubnet(): string {\n return this.getBitsBase2(this.subnetMask, constants6.BITS);\n }\n\n /**\n * Return the reversed ip6.arpa form of the address\n * @memberof Address6\n * @param {Object} options\n * @param {boolean} options.omitSuffix - omit the \"ip6.arpa\" suffix\n * @instance\n * @returns {String}\n */\n reverseForm(options?: common.ReverseFormOptions): string {\n if (!options) {\n options = {};\n }\n\n const characters = Math.floor(this.subnetMask / 4);\n\n const reversed = this.canonicalForm()\n .replace(/:/g, '')\n .split('')\n .slice(0, characters)\n .reverse()\n .join('.');\n\n if (characters > 0) {\n if (options.omitSuffix) {\n return reversed;\n }\n\n return `${reversed}.ip6.arpa.`;\n }\n\n if (options.omitSuffix) {\n return '';\n }\n\n return 'ip6.arpa.';\n }\n\n /**\n * Return the correct form of the address\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n correctForm(): string {\n let i;\n let groups = [];\n\n let zeroCounter = 0;\n const zeroes = [];\n\n for (i = 0; i < this.parsedAddress.length; i++) {\n const value = parseInt(this.parsedAddress[i], 16);\n\n if (value === 0) {\n zeroCounter++;\n }\n\n if (value !== 0 && zeroCounter > 0) {\n if (zeroCounter > 1) {\n zeroes.push([i - zeroCounter, i - 1]);\n }\n\n zeroCounter = 0;\n }\n }\n\n // Do we end with a string of zeroes?\n if (zeroCounter > 1) {\n zeroes.push([this.parsedAddress.length - zeroCounter, this.parsedAddress.length - 1]);\n }\n\n const zeroLengths = zeroes.map((n) => n[1] - n[0] + 1);\n\n if (zeroes.length > 0) {\n const index = zeroLengths.indexOf(Math.max(...zeroLengths) as number);\n\n groups = compact(this.parsedAddress, zeroes[index]);\n } else {\n groups = this.parsedAddress;\n }\n\n for (i = 0; i < groups.length; i++) {\n if (groups[i] !== 'compact') {\n groups[i] = parseInt(groups[i], 16).toString(16);\n }\n }\n\n let correct = groups.join(':');\n\n correct = correct.replace(/^compact$/, '::');\n correct = correct.replace(/(^compact)|(compact$)/, ':');\n correct = correct.replace(/compact/, '');\n\n return correct;\n }\n\n /**\n * Return a zero-padded base-2 string representation of the address\n * @memberof Address6\n * @instance\n * @returns {String}\n * @example\n * var address = new Address6('2001:4860:4001:803::1011');\n * address.binaryZeroPad();\n * // '0010000000000001010010000110000001000000000000010000100000000011\n * // 0000000000000000000000000000000000000000000000000001000000010001'\n */\n binaryZeroPad(): string {\n return this.bigInt().toString(2).padStart(constants6.BITS, '0');\n }\n\n // TODO: Improve the semantics of this helper function\n parse4in6(address: string): string {\n const groups = address.split(':');\n const lastGroup = groups.slice(-1)[0];\n\n const address4 = lastGroup.match(constants4.RE_ADDRESS);\n\n if (address4) {\n this.parsedAddress4 = address4[0];\n this.address4 = new Address4(this.parsedAddress4);\n\n for (let i = 0; i < this.address4.groups; i++) {\n if (/^0[0-9]+/.test(this.address4.parsedAddress[i])) {\n throw new AddressError(\n \"IPv4 addresses can't have leading zeroes.\",\n address.replace(\n constants4.RE_ADDRESS,\n this.address4.parsedAddress.map(spanLeadingZeroes4).join('.'),\n ),\n );\n }\n }\n\n this.v4 = true;\n\n groups[groups.length - 1] = this.address4.toGroup6();\n\n address = groups.join(':');\n }\n\n return address;\n }\n\n // TODO: Make private?\n parse(address: string): string[] {\n address = this.parse4in6(address);\n\n const badCharacters = address.match(constants6.RE_BAD_CHARACTERS);\n\n if (badCharacters) {\n throw new AddressError(\n `Bad character${\n badCharacters.length > 1 ? 's' : ''\n } detected in address: ${badCharacters.join('')}`,\n address.replace(constants6.RE_BAD_CHARACTERS, '<span class=\"parse-error\">$1</span>'),\n );\n }\n\n const badAddress = address.match(constants6.RE_BAD_ADDRESS);\n\n if (badAddress) {\n throw new AddressError(\n `Address failed regex: ${badAddress.join('')}`,\n address.replace(constants6.RE_BAD_ADDRESS, '<span class=\"parse-error\">$1</span>'),\n );\n }\n\n let groups: string[] = [];\n\n const halves = address.split('::');\n\n if (halves.length === 2) {\n let first = halves[0].split(':');\n let last = halves[1].split(':');\n\n if (first.length === 1 && first[0] === '') {\n first = [];\n }\n\n if (last.length === 1 && last[0] === '') {\n last = [];\n }\n\n const remaining = this.groups - (first.length + last.length);\n\n if (!remaining) {\n throw new AddressError('Error parsing groups');\n }\n\n this.elidedGroups = remaining;\n\n this.elisionBegin = first.length;\n this.elisionEnd = first.length + this.elidedGroups;\n\n groups = groups.concat(first);\n\n for (let i = 0; i < remaining; i++) {\n groups.push('0');\n }\n\n groups = groups.concat(last);\n } else if (halves.length === 1) {\n groups = address.split(':');\n\n this.elidedGroups = 0;\n } else {\n throw new AddressError('Too many :: groups found');\n }\n\n groups = groups.map((group: string) => parseInt(group, 16).toString(16));\n\n if (groups.length !== this.groups) {\n throw new AddressError('Incorrect number of groups found');\n }\n\n return groups;\n }\n\n /**\n * Return the canonical form of the address\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n canonicalForm(): string {\n return this.parsedAddress.map(paddedHex).join(':');\n }\n\n /**\n * Return the decimal form of the address\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n decimal(): string {\n return this.parsedAddress.map((n) => parseInt(n, 16).toString(10).padStart(5, '0')).join(':');\n }\n\n /**\n * Return the address as a BigInt\n * @memberof Address6\n * @instance\n * @returns {bigint}\n */\n bigInt(): bigint {\n return BigInt(`0x${this.parsedAddress.map(paddedHex).join('')}`);\n }\n\n /**\n * Return the last two groups of this address as an IPv4 address string\n * @memberof Address6\n * @instance\n * @returns {Address4}\n * @example\n * var address = new Address6('2001:4860:4001::1825:bf11');\n * address.to4().correctForm(); // '24.37.191.17'\n */\n to4(): Address4 {\n const binary = this.binaryZeroPad().split('');\n\n return Address4.fromHex(BigInt(`0b${binary.slice(96, 128).join('')}`).toString(16));\n }\n\n /**\n * Return the v4-in-v6 form of the address\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n to4in6(): string {\n const address4 = this.to4();\n const address6 = new Address6(this.parsedAddress.slice(0, 6).join(':'), 6);\n\n const correct = address6.correctForm();\n\n let infix = '';\n\n if (!/:$/.test(correct)) {\n infix = ':';\n }\n\n return correct + infix + address4.address;\n }\n\n /**\n * Return an object containing the Teredo properties of the address\n * @memberof Address6\n * @instance\n * @returns {Object}\n */\n inspectTeredo(): TeredoProperties {\n /*\n - Bits 0 to 31 are set to the Teredo prefix (normally 2001:0000::/32).\n - Bits 32 to 63 embed the primary IPv4 address of the Teredo server that\n is used.\n - Bits 64 to 79 can be used to define some flags. Currently only the\n higher order bit is used; it is set to 1 if the Teredo client is\n located behind a cone NAT, 0 otherwise. For Microsoft's Windows Vista\n and Windows Server 2008 implementations, more bits are used. In those\n implementations, the format for these 16 bits is \"CRAAAAUG AAAAAAAA\",\n where \"C\" remains the \"Cone\" flag. The \"R\" bit is reserved for future\n use. The \"U\" bit is for the Universal/Local flag (set to 0). The \"G\" bit\n is Individual/Group flag (set to 0). The A bits are set to a 12-bit\n randomly generated number chosen by the Teredo client to introduce\n additional protection for the Teredo node against IPv6-based scanning\n attacks.\n - Bits 80 to 95 contains the obfuscated UDP port number. This is the\n port number that is mapped by the NAT to the Teredo client with all\n bits inverted.\n - Bits 96 to 127 contains the obfuscated IPv4 address. This is the\n public IPv4 address of the NAT with all bits inverted.\n */\n const prefix = this.getBitsBase16(0, 32);\n\n const bitsForUdpPort: bigint = this.getBits(80, 96);\n // eslint-disable-next-line no-bitwise\n const udpPort = (bitsForUdpPort ^ BigInt('0xffff')).toString();\n\n const server4 = Address4.fromHex(this.getBitsBase16(32, 64));\n\n const bitsForClient4 = this.getBits(96, 128);\n // eslint-disable-next-line no-bitwise\n const client4 = Address4.fromHex((bitsForClient4 ^ BigInt('0xffffffff')).toString(16));\n\n const flagsBase2 = this.getBitsBase2(64, 80);\n\n const coneNat = testBit(flagsBase2, 15);\n const reserved = testBit(flagsBase2, 14);\n const groupIndividual = testBit(flagsBase2, 8);\n const universalLocal = testBit(flagsBase2, 9);\n const nonce = BigInt(`0b${flagsBase2.slice(2, 6) + flagsBase2.slice(8, 16)}`).toString(10);\n\n return {\n prefix: `${prefix.slice(0, 4)}:${prefix.slice(4, 8)}`,\n server4: server4.address,\n client4: client4.address,\n flags: flagsBase2,\n coneNat,\n microsoft: {\n reserved,\n universalLocal,\n groupIndividual,\n nonce,\n },\n udpPort,\n };\n }\n\n /**\n * Return an object containing the 6to4 properties of the address\n * @memberof Address6\n * @instance\n * @returns {Object}\n */\n inspect6to4(): SixToFourProperties {\n /*\n - Bits 0 to 15 are set to the 6to4 prefix (2002::/16).\n - Bits 16 to 48 embed the IPv4 address of the 6to4 gateway that is used.\n */\n\n const prefix = this.getBitsBase16(0, 16);\n\n const gateway = Address4.fromHex(this.getBitsBase16(16, 48));\n\n return {\n prefix: prefix.slice(0, 4),\n gateway: gateway.address,\n };\n }\n\n /**\n * Return a v6 6to4 address from a v6 v4inv6 address\n * @memberof Address6\n * @instance\n * @returns {Address6}\n */\n to6to4(): Address6 | null {\n if (!this.is4()) {\n return null;\n }\n\n const addr6to4 = [\n '2002',\n this.getBitsBase16(96, 112),\n this.getBitsBase16(112, 128),\n '',\n '/16',\n ].join(':');\n\n return new Address6(addr6to4);\n }\n\n /**\n * Return a byte array\n * @memberof Address6\n * @instance\n * @returns {Array}\n */\n toByteArray(): number[] {\n const valueWithoutPadding = this.bigInt().toString(16);\n const leadingPad = '0'.repeat(valueWithoutPadding.length % 2);\n\n const value = `${leadingPad}${valueWithoutPadding}`;\n\n const bytes = [];\n for (let i = 0, length = value.length; i < length; i += 2) {\n bytes.push(parseInt(value.substring(i, i + 2), 16));\n }\n\n return bytes;\n }\n\n /**\n * Return an unsigned byte array\n * @memberof Address6\n * @instance\n * @returns {Array}\n */\n toUnsignedByteArray(): number[] {\n return this.toByteArray().map(unsignByte);\n }\n\n /**\n * Convert a byte array to an Address6 object\n * @memberof Address6\n * @static\n * @returns {Address6}\n */\n static fromByteArray(bytes: Array<any>): Address6 {\n return this.fromUnsignedByteArray(bytes.map(unsignByte));\n }\n\n /**\n * Convert an unsigned byte array to an Address6 object\n * @memberof Address6\n * @static\n * @returns {Address6}\n */\n static fromUnsignedByteArray(bytes: Array<any>): Address6 {\n const BYTE_MAX = BigInt('256');\n let result = BigInt('0');\n let multiplier = BigInt('1');\n\n for (let i = bytes.length - 1; i >= 0; i--) {\n result += multiplier * BigInt(bytes[i].toString(10));\n\n multiplier *= BYTE_MAX;\n }\n\n return Address6.fromBigInt(result);\n }\n\n // #region Attributes\n /**\n * Returns true if the given address is in the subnet of the current address\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n isInSubnet = common.isInSubnet;\n\n /**\n * Returns true if the address is correct, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n isCorrect = common.isCorrect(constants6.BITS);\n\n /**\n * Returns true if the address is in the canonical form, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n isCanonical(): boolean {\n return this.addressMinusSuffix === this.canonicalForm();\n }\n\n /**\n * Returns true if the address is a link local address, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n isLinkLocal(): boolean {\n // Zeroes are required, i.e. we can't check isInSubnet with 'fe80::/10'\n if (\n this.getBitsBase2(0, 64) ===\n '1111111010000000000000000000000000000000000000000000000000000000'\n ) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Returns true if the address is a multicast address, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n isMulticast(): boolean {\n return this.getType() === 'Multicast';\n }\n\n /**\n * Returns true if the address is a v4-in-v6 address, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n is4(): boolean {\n return this.v4;\n }\n\n /**\n * Returns true if the address is a Teredo address, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n isTeredo(): boolean {\n return this.isInSubnet(new Address6('2001::/32'));\n }\n\n /**\n * Returns true if the address is a 6to4 address, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n is6to4(): boolean {\n return this.isInSubnet(new Address6('2002::/16'));\n }\n\n /**\n * Returns true if the address is a loopback address, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n isLoopback(): boolean {\n return this.getType() === 'Loopback';\n }\n // #endregion\n\n // #region HTML\n /**\n * @returns {String} the address in link form with a default port of 80\n */\n href(optionalPort?: number | string): string {\n if (optionalPort === undefined) {\n optionalPort = '';\n } else {\n optionalPort = `:${optionalPort}`;\n }\n\n return `http://[${this.correctForm()}]${optionalPort}/`;\n }\n\n /**\n * @returns {String} a link suitable for conveying the address via a URL hash\n */\n link(options?: { className?: string; prefix?: string; v4?: boolean }): string {\n if (!options) {\n options = {};\n }\n\n if (options.className === undefined) {\n options.className = '';\n }\n\n if (options.prefix === undefined) {\n options.prefix = '/#address=';\n }\n\n if (options.v4 === undefined) {\n options.v4 = false;\n }\n\n let formFunction = this.correctForm;\n\n if (options.v4) {\n formFunction = this.to4in6;\n }\n\n const form = formFunction.call(this);\n\n if (options.className) {\n return `<a href=\"${options.prefix}${form}\" class=\"${options.className}\">${form}</a>`;\n }\n\n return `<a href=\"${options.prefix}${form}\">${form}</a>`;\n }\n\n /**\n * Groups an address\n * @returns {String}\n */\n group(): string {\n if (this.elidedGroups === 0) {\n // The simple case\n return helpers.simpleGroup(this.address).join(':');\n }\n\n assert(typeof this.elidedGroups === 'number');\n assert(typeof this.elisionBegin === 'number');\n\n // The elided case\n const output = [];\n\n const [left, right] = this.address.split('::');\n\n if (left.length) {\n output.push(...helpers.simpleGroup(left));\n } else {\n output.push('');\n }\n\n const classes = ['hover-group'];\n\n for (let i = this.elisionBegin; i < this.elisionBegin + this.elidedGroups; i++) {\n classes.push(`group-${i}`);\n }\n\n output.push(`<span class=\"${classes.join(' ')}\"></span>`);\n\n if (right.length) {\n output.push(...helpers.simpleGroup(right, this.elisionEnd));\n } else {\n output.push('');\n }\n\n if (this.is4()) {\n assert(this.address4 instanceof Address4);\n\n output.pop();\n output.push(this.address4.groupForV6());\n }\n\n return output.join(':');\n }\n // #endregion\n\n // #region Regular expressions\n /**\n * Generate a regular expression string that can be used to find or validate\n * all variations of this address\n * @memberof Address6\n * @instance\n * @param {boolean} substringSearch\n * @returns {string}\n */\n regularExpressionString(this: Address6, substringSearch: boolean = false): string {\n let output: string[] = [];\n\n // TODO: revisit why this is necessary\n const address6 = new Address6(this.correctForm());\n\n if (address6.elidedGroups === 0) {\n // The simple case\n output.push(simpleRegularExpression(address6.parsedAddress));\n } else if (address6.elidedGroups === constants6.GROUPS) {\n // A completely elided address\n output.push(possibleElisions(constants6.GROUPS));\n } else {\n // A partially elided address\n const halves = address6.address.split('::');\n\n if (halves[0].length) {\n output.push(simpleRegularExpression(halves[0].split(':')));\n }\n\n assert(typeof address6.elidedGroups === 'number');\n\n output.push(\n possibleElisions(address6.elidedGroups, halves[0].length !== 0, halves[1].length !== 0),\n );\n\n if (halves[1].length) {\n output.push(simpleRegularExpression(halves[1].split(':')));\n }\n\n output = [output.join(':')];\n }\n\n if (!substringSearch) {\n output = [\n '(?=^|',\n ADDRESS_BOUNDARY,\n '|[^\\\\w\\\\:])(',\n ...output,\n ')(?=[^\\\\w\\\\:]|',\n ADDRESS_BOUNDARY,\n '|$)',\n ];\n }\n\n return output.join('');\n }\n\n /**\n * Generate a regular expression that can be used to find or validate all\n * variations of this address.\n * @memberof Address6\n * @instance\n * @param {boolean} substringSearch\n * @returns {RegExp}\n */\n regularExpression(this: Address6, substringSearch: boolean = false): RegExp {\n return new RegExp(this.regularExpressionString(substringSearch), 'i');\n }\n // #endregion\n}\n", "export { Address4 } from './ipv4';\nexport { Address6 } from './ipv6';\nexport { AddressError } from './address-error';\n\nimport * as helpers from './v6/helpers';\n\nexport const v6 = { helpers };\n", "import { Database } from 'bun:sqlite';\nimport { existsSync, statSync } from 'fs';\nimport type { Observation, Summary, SearchFilters, TimelineEntry } from '../../types/worker-types.js';\n\n/**\n * Advanced search module for Kiro Memory\n * Supports FTS5 full-text search with LIKE fallback\n */\n\n/**\n * BM25 weights for FTS5 columns: title, text, narrative, concepts.\n * Higher values = more relevant column in ranking.\n */\nconst BM25_WEIGHTS = '10.0, 1.0, 5.0, 3.0';\n\n/** Escape LIKE wildcard characters to prevent pattern injection */\nfunction escapeLikePattern(input: string): string {\n return input.replace(/[%_\\\\]/g, '\\\\$&');\n}\n\n/**\n * Sanitize a query for FTS5: wraps each term in quotes\n * to prevent reserved operators (AND, OR, NOT, NEAR, *, ^, :)\n * from causing parsing errors. Limits length and term count to prevent ReDoS.\n */\nfunction sanitizeFTS5Query(query: string): string {\n // Limit input length before parsing\n const trimmed = query.length > 10_000 ? query.substring(0, 10_000) : query;\n\n const terms = trimmed\n .replace(/[\"\"\\u0022]/g, '')\n .split(/\\s+/)\n .filter(t => t.length > 0)\n .slice(0, 100)\n .map(t => `\"${t}\"`);\n\n return terms.join(' ');\n}\n\n/**\n * Search observations with FTS5 (full-text) and optional filters.\n * Sanitizes the FTS5 query and falls back to LIKE on error.\n */\nexport function searchObservationsFTS(\n db: Database,\n query: string,\n filters: SearchFilters = {}\n): Observation[] {\n const limit = filters.limit || 50;\n\n try {\n const safeQuery = sanitizeFTS5Query(query);\n if (!safeQuery) return searchObservationsLIKE(db, query, filters);\n\n let sql = `\n SELECT o.* FROM observations o\n JOIN observations_fts fts ON o.id = fts.rowid\n WHERE observations_fts MATCH ?\n `;\n const params: (string | number)[] = [safeQuery];\n\n if (filters.project) {\n sql += ' AND o.project = ?';\n params.push(filters.project);\n }\n if (filters.type) {\n sql += ' AND o.type = ?';\n params.push(filters.type);\n }\n if (filters.dateStart) {\n sql += ' AND o.created_at_epoch >= ?';\n params.push(filters.dateStart);\n }\n if (filters.dateEnd) {\n sql += ' AND o.created_at_epoch <= ?';\n params.push(filters.dateEnd);\n }\n\n sql += ` ORDER BY bm25(observations_fts, ${BM25_WEIGHTS}) LIMIT ?`;\n params.push(limit);\n\n const stmt = db.query(sql);\n return stmt.all(...params) as Observation[];\n } catch {\n // Fallback to LIKE if FTS5 is unavailable or query is malformed\n return searchObservationsLIKE(db, query, filters);\n }\n}\n\n/**\n * FTS5 search that exposes the raw rank for scoring.\n * The FTS5 rank is negative: more negative = more relevant.\n * Uses sanitizeFTS5Query for safety, falls back to LIKE without rank.\n */\nexport function searchObservationsFTSWithRank(\n db: Database,\n query: string,\n filters: SearchFilters = {}\n): Array<Observation & { fts5_rank: number }> {\n const limit = filters.limit || 50;\n\n try {\n const safeQuery = sanitizeFTS5Query(query);\n if (!safeQuery) return [];\n\n let sql = `\n SELECT o.*, bm25(observations_fts, ${BM25_WEIGHTS}) as fts5_rank FROM observations o\n JOIN observations_fts fts ON o.id = fts.rowid\n WHERE observations_fts MATCH ?\n `;\n const params: (string | number)[] = [safeQuery];\n\n if (filters.project) {\n sql += ' AND o.project = ?';\n params.push(filters.project);\n }\n if (filters.type) {\n sql += ' AND o.type = ?';\n params.push(filters.type);\n }\n if (filters.dateStart) {\n sql += ' AND o.created_at_epoch >= ?';\n params.push(filters.dateStart);\n }\n if (filters.dateEnd) {\n sql += ' AND o.created_at_epoch <= ?';\n params.push(filters.dateEnd);\n }\n\n sql += ` ORDER BY bm25(observations_fts, ${BM25_WEIGHTS}) LIMIT ?`;\n params.push(limit);\n\n const stmt = db.query(sql);\n return stmt.all(...params) as Array<Observation & { fts5_rank: number }>;\n } catch {\n // Fallback: no rank available\n return [];\n }\n}\n\n/**\n * Fallback: LIKE search on observations\n */\nexport function searchObservationsLIKE(\n db: Database,\n query: string,\n filters: SearchFilters = {}\n): Observation[] {\n const limit = filters.limit || 50;\n const pattern = `%${escapeLikePattern(query)}%`;\n let sql = `\n SELECT * FROM observations\n WHERE (title LIKE ? ESCAPE '\\\\' OR text LIKE ? ESCAPE '\\\\' OR narrative LIKE ? ESCAPE '\\\\' OR concepts LIKE ? ESCAPE '\\\\')\n `;\n const params: (string | number)[] = [pattern, pattern, pattern, pattern];\n\n if (filters.project) {\n sql += ' AND project = ?';\n params.push(filters.project);\n }\n if (filters.type) {\n sql += ' AND type = ?';\n params.push(filters.type);\n }\n if (filters.dateStart) {\n sql += ' AND created_at_epoch >= ?';\n params.push(filters.dateStart);\n }\n if (filters.dateEnd) {\n sql += ' AND created_at_epoch <= ?';\n params.push(filters.dateEnd);\n }\n\n sql += ' ORDER BY created_at_epoch DESC LIMIT ?';\n params.push(limit);\n\n const stmt = db.query(sql);\n return stmt.all(...params) as Observation[];\n}\n\n/**\n * Search summaries with filters\n */\nexport function searchSummariesFiltered(\n db: Database,\n query: string,\n filters: SearchFilters = {}\n): Summary[] {\n const limit = filters.limit || 20;\n const pattern = `%${escapeLikePattern(query)}%`;\n let sql = `\n SELECT * FROM summaries\n WHERE (request LIKE ? ESCAPE '\\\\' OR learned LIKE ? ESCAPE '\\\\' OR completed LIKE ? ESCAPE '\\\\' OR notes LIKE ? ESCAPE '\\\\' OR next_steps LIKE ? ESCAPE '\\\\')\n `;\n const params: (string | number)[] = [pattern, pattern, pattern, pattern, pattern];\n\n if (filters.project) {\n sql += ' AND project = ?';\n params.push(filters.project);\n }\n if (filters.dateStart) {\n sql += ' AND created_at_epoch >= ?';\n params.push(filters.dateStart);\n }\n if (filters.dateEnd) {\n sql += ' AND created_at_epoch <= ?';\n params.push(filters.dateEnd);\n }\n\n sql += ' ORDER BY created_at_epoch DESC LIMIT ?';\n params.push(limit);\n\n const stmt = db.query(sql);\n return stmt.all(...params) as Summary[];\n}\n\n/**\n * Retrieve observations by ID (batch)\n */\nexport function getObservationsByIds(db: Database, ids: number[]): Observation[] {\n if (!Array.isArray(ids) || ids.length === 0) return [];\n\n // Validate and filter: only positive integers, max 500 per query\n const validIds = ids\n .filter(id => typeof id === 'number' && Number.isInteger(id) && id > 0)\n .slice(0, 500);\n\n if (validIds.length === 0) return [];\n\n const placeholders = validIds.map(() => '?').join(',');\n const sql = `SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch DESC`;\n const stmt = db.query(sql);\n return stmt.all(...validIds) as Observation[];\n}\n\n/**\n * Timeline: chronological context around an observation\n */\nexport function getTimeline(\n db: Database,\n anchorId: number,\n depthBefore: number = 5,\n depthAfter: number = 5\n): TimelineEntry[] {\n // Find the anchor's epoch\n const anchorStmt = db.query('SELECT created_at_epoch FROM observations WHERE id = ?');\n const anchor = anchorStmt.get(anchorId) as { created_at_epoch: number } | null;\n\n if (!anchor) return [];\n\n const anchorEpoch = anchor.created_at_epoch;\n\n // Observations before\n const beforeStmt = db.query(`\n SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch\n FROM observations\n WHERE created_at_epoch < ?\n ORDER BY created_at_epoch DESC\n LIMIT ?\n `);\n const before = (beforeStmt.all(anchorEpoch, depthBefore) as TimelineEntry[]).reverse();\n\n // The anchor itself\n const selfStmt = db.query(`\n SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch\n FROM observations WHERE id = ?\n `);\n const self = selfStmt.all(anchorId) as TimelineEntry[];\n\n // Observations after\n const afterStmt = db.query(`\n SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch\n FROM observations\n WHERE created_at_epoch > ?\n ORDER BY created_at_epoch ASC\n LIMIT ?\n `);\n const after = afterStmt.all(anchorEpoch, depthAfter) as TimelineEntry[];\n\n return [...before, ...self, ...after];\n}\n\n/**\n * Database statistics for a project.\n * Single CTE query replacing 6 separate queries.\n */\nexport function getProjectStats(db: Database, project: string): {\n observations: number;\n summaries: number;\n sessions: number;\n prompts: number;\n tokenEconomics: { discoveryTokens: number; readTokens: number; savings: number };\n} {\n const sql = `\n WITH\n obs_stats AS (\n SELECT\n COUNT(*) as count,\n COALESCE(SUM(discovery_tokens), 0) as discovery_tokens,\n COALESCE(SUM(\n CAST((LENGTH(COALESCE(title, '')) + LENGTH(COALESCE(narrative, ''))) / 4 AS INTEGER)\n ), 0) as read_tokens\n FROM observations WHERE project = ?\n ),\n sum_count AS (SELECT COUNT(*) as count FROM summaries WHERE project = ?),\n ses_count AS (SELECT COUNT(*) as count FROM sessions WHERE project = ?),\n prm_count AS (SELECT COUNT(*) as count FROM prompts WHERE project = ?)\n SELECT\n obs_stats.count as observations,\n obs_stats.discovery_tokens,\n obs_stats.read_tokens,\n sum_count.count as summaries,\n ses_count.count as sessions,\n prm_count.count as prompts\n FROM obs_stats, sum_count, ses_count, prm_count\n `;\n\n const row = db.query(sql).get(project, project, project, project) as any;\n\n const discoveryTokens = row?.discovery_tokens || 0;\n const readTokens = row?.read_tokens || 0;\n const savings = Math.max(0, discoveryTokens - readTokens);\n\n return {\n observations: row?.observations || 0,\n summaries: row?.summaries || 0,\n sessions: row?.sessions || 0,\n prompts: row?.prompts || 0,\n tokenEconomics: { discoveryTokens, readTokens, savings },\n };\n}\n\n/**\n * Find observations with files modified after the observation was created.\n * Checks the filesystem mtime for each file in files_modified.\n */\nexport function getStaleObservations(db: Database, project: string): Observation[] {\n // Query observations with non-empty files_modified\n const rows = db.query(`\n SELECT * FROM observations\n WHERE project = ? AND files_modified IS NOT NULL AND files_modified != ''\n ORDER BY created_at_epoch DESC\n LIMIT 500\n `).all(project) as Observation[];\n\n const staleObs: Observation[] = [];\n\n for (const obs of rows) {\n if (!obs.files_modified) continue;\n\n // Parse files_modified (comma-separated)\n const files = obs.files_modified.split(',').map(f => f.trim()).filter(Boolean);\n\n let isStale = false;\n for (const filepath of files) {\n try {\n if (!existsSync(filepath)) continue; // File removed, cannot verify\n const stat = statSync(filepath);\n if (stat.mtimeMs > obs.created_at_epoch) {\n isStale = true;\n break;\n }\n } catch {\n // File not accessible, skip\n }\n }\n\n if (isStale) {\n staleObs.push(obs);\n }\n }\n\n return staleObs;\n}\n\n/**\n * Mark observations as stale (1) or fresh (0) in the database.\n */\nexport function markObservationsStale(db: Database, ids: number[], stale: boolean): void {\n if (!Array.isArray(ids) || ids.length === 0) return;\n\n const validIds = ids\n .filter(id => typeof id === 'number' && Number.isInteger(id) && id > 0)\n .slice(0, 500);\n\n if (validIds.length === 0) return;\n\n const placeholders = validIds.map(() => '?').join(',');\n db.run(\n `UPDATE observations SET is_stale = ? WHERE id IN (${placeholders})`,\n [stale ? 1 : 0, ...validIds]\n );\n}\n", "import { Database } from 'bun:sqlite';\nimport type { Observation } from '../../types/worker-types.js';\n\n/**\n * Observation operations for Kiro Memory database\n */\n\n/** Escape LIKE wildcard characters to prevent pattern injection */\nfunction escapeLikePattern(input: string): string {\n return input.replace(/[%_\\\\]/g, '\\\\$&');\n}\n\n/**\n * Check if an observation with the same content_hash exists within the last 30 seconds.\n * Returns true if it is a duplicate (to be discarded).\n */\nexport function isDuplicateObservation(db: Database, contentHash: string, windowMs: number = 30_000): boolean {\n if (!contentHash) return false;\n const threshold = Date.now() - windowMs;\n const result = db.query(\n 'SELECT id FROM observations WHERE content_hash = ? AND created_at_epoch > ? LIMIT 1'\n ).get(contentHash, threshold);\n return !!result;\n}\n\nexport function createObservation(\n db: Database,\n memorySessionId: string,\n project: string,\n type: string,\n title: string,\n subtitle: string | null,\n text: string | null,\n narrative: string | null,\n facts: string | null,\n concepts: string | null,\n filesRead: string | null,\n filesModified: string | null,\n promptNumber: number,\n contentHash: string | null = null,\n discoveryTokens: number = 0\n): number {\n const now = new Date();\n const result = db.run(\n `INSERT INTO observations\n (memory_session_id, project, type, title, subtitle, text, narrative, facts, concepts, files_read, files_modified, prompt_number, created_at, created_at_epoch, content_hash, discovery_tokens)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber, now.toISOString(), now.getTime(), contentHash, discoveryTokens]\n );\n return Number(result.lastInsertRowid);\n}\n\nexport function getObservationsBySession(db: Database, memorySessionId: string): Observation[] {\n const query = db.query(\n 'SELECT * FROM observations WHERE memory_session_id = ? ORDER BY prompt_number ASC'\n );\n return query.all(memorySessionId) as Observation[];\n}\n\nexport function getObservationsByProject(db: Database, project: string, limit: number = 100): Observation[] {\n const query = db.query(\n 'SELECT * FROM observations WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ?'\n );\n return query.all(project, limit) as Observation[];\n}\n\nexport function searchObservations(db: Database, searchTerm: string, project?: string): Observation[] {\n const sql = project\n ? `SELECT * FROM observations\n WHERE project = ? AND (title LIKE ? ESCAPE '\\\\' OR text LIKE ? ESCAPE '\\\\' OR narrative LIKE ? ESCAPE '\\\\')\n ORDER BY created_at_epoch DESC`\n : `SELECT * FROM observations\n WHERE title LIKE ? ESCAPE '\\\\' OR text LIKE ? ESCAPE '\\\\' OR narrative LIKE ? ESCAPE '\\\\'\n ORDER BY created_at_epoch DESC`;\n\n const pattern = `%${escapeLikePattern(searchTerm)}%`;\n const query = db.query(sql);\n\n if (project) {\n return query.all(project, pattern, pattern, pattern) as Observation[];\n }\n return query.all(pattern, pattern, pattern) as Observation[];\n}\n\nexport function deleteObservation(db: Database, id: number): void {\n db.run('DELETE FROM observations WHERE id = ?', [id]);\n}\n\n/**\n * Update the last access timestamp for observations found in search.\n * Fire-and-forget: non-blocking, ignores errors.\n */\nexport function updateLastAccessed(db: Database, ids: number[]): void {\n if (!Array.isArray(ids) || ids.length === 0) return;\n\n const validIds = ids\n .filter(id => typeof id === 'number' && Number.isInteger(id) && id > 0)\n .slice(0, 500);\n\n if (validIds.length === 0) return;\n\n const now = Date.now();\n const placeholders = validIds.map(() => '?').join(',');\n db.run(\n `UPDATE observations SET last_accessed_epoch = ? WHERE id IN (${placeholders})`,\n [now, ...validIds]\n );\n}\n\n/**\n * Consolidate duplicate observations on the same file and type.\n * Groups by (project, type, files_modified), keeps the most recent,\n * concatenates unique contents, deletes the old ones.\n *\n * Fix: counts calculated inside the transaction and returned directly,\n * dry-run separated from the transaction to avoid unnecessary locks.\n */\nexport function consolidateObservations(\n db: Database,\n project: string,\n options: { dryRun?: boolean; minGroupSize?: number } = {}\n): { merged: number; removed: number } {\n const minGroupSize = options.minGroupSize || 3;\n\n // Find groups of observations with the same (project, type, files_modified)\n const groups = db.query(`\n SELECT type, files_modified, COUNT(*) as cnt, GROUP_CONCAT(id) as ids\n FROM observations\n WHERE project = ? AND files_modified IS NOT NULL AND files_modified != ''\n GROUP BY type, files_modified\n HAVING cnt >= ?\n ORDER BY cnt DESC\n `).all(project, minGroupSize) as Array<{\n type: string;\n files_modified: string;\n cnt: number;\n ids: string;\n }>;\n\n if (groups.length === 0) return { merged: 0, removed: 0 };\n\n // Dry-run: calculate counts without opening a transaction\n if (options.dryRun) {\n let totalMerged = 0;\n let totalRemoved = 0;\n\n for (const group of groups) {\n const obsIds = group.ids.split(',').map(Number);\n const placeholders = obsIds.map(() => '?').join(',');\n const count = (db.query(\n `SELECT COUNT(*) as cnt FROM observations WHERE id IN (${placeholders})`\n ).get(...obsIds) as { cnt: number })?.cnt || 0;\n\n if (count >= minGroupSize) {\n totalMerged += 1;\n totalRemoved += count - 1;\n }\n }\n\n return { merged: totalMerged, removed: totalRemoved };\n }\n\n // Execute consolidation in an atomic transaction.\n // Counts are calculated and returned by the transaction itself,\n // so if it fails no partial values remain.\n const runConsolidation = db.transaction(() => {\n let merged = 0;\n let removed = 0;\n\n for (const group of groups) {\n const obsIds = group.ids.split(',').map(Number);\n const placeholders = obsIds.map(() => '?').join(',');\n const observations = db.query(\n `SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch DESC`\n ).all(...obsIds) as Observation[];\n\n if (observations.length < minGroupSize) continue;\n\n // Keep the most recent, concatenate unique contents from the others\n const keeper = observations[0];\n const others = observations.slice(1);\n\n const uniqueTexts = new Set<string>();\n if (keeper.text) uniqueTexts.add(keeper.text);\n for (const obs of others) {\n if (obs.text && !uniqueTexts.has(obs.text)) {\n uniqueTexts.add(obs.text);\n }\n }\n\n // Update the keeper with consolidated text\n const consolidatedText = Array.from(uniqueTexts).join('\\n---\\n').substring(0, 100_000);\n db.run(\n 'UPDATE observations SET text = ?, title = ? WHERE id = ?',\n [consolidatedText, `[consolidated x${observations.length}] ${keeper.title}`, keeper.id]\n );\n\n // Delete old observations (and their embeddings)\n const removeIds = others.map(o => o.id);\n const removePlaceholders = removeIds.map(() => '?').join(',');\n db.run(`DELETE FROM observations WHERE id IN (${removePlaceholders})`, removeIds);\n db.run(`DELETE FROM observation_embeddings WHERE observation_id IN (${removePlaceholders})`, removeIds);\n\n merged += 1;\n removed += removeIds.length;\n }\n\n return { merged, removed };\n });\n\n return runConsolidation();\n}\n", "/**\n * Kiro Memory Worker Service\n *\n * Lean orchestrator: configures Express, mounts modular routers,\n * manages lifecycle (PID, shutdown, error handling).\n *\n * Routes defined in src/services/routes/:\n * core.ts \u2014 health, SSE, notify\n * observations.ts \u2014 CRUD observations, knowledge, memory save\n * summaries.ts \u2014 CRUD summaries\n * search.ts \u2014 FTS5, hybrid search, timeline\n * analytics.ts \u2014 overview, timeline, types, sessions\n * sessions.ts \u2014 sessions, checkpoint, prompts\n * projects.ts \u2014 project list, aliases, stats\n * data.ts \u2014 embeddings, retention, export, report\n */\n\nimport express from 'express';\nimport cors from 'cors';\nimport helmet from 'helmet';\nimport rateLimit from 'express-rate-limit';\nimport crypto from 'crypto';\nimport { join, dirname } from 'path';\nimport { existsSync, mkdirSync, writeFileSync, unlinkSync, chmodSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { KiroMemoryDatabase } from './sqlite/Database.js';\nimport { getHybridSearch } from './search/HybridSearch.js';\nimport { createWorkerContext, getClients } from './worker-context.js';\nimport { logger } from '../utils/logger.js';\nimport { DATA_DIR } from '../shared/paths.js';\n\n// Modular routers\nimport { createCoreRouter } from './routes/core.js';\nimport { createObservationsRouter } from './routes/observations.js';\nimport { createSummariesRouter } from './routes/summaries.js';\nimport { createSearchRouter } from './routes/search.js';\nimport { createAnalyticsRouter } from './routes/analytics.js';\nimport { createSessionsRouter } from './routes/sessions.js';\nimport { createProjectsRouter } from './routes/projects.js';\nimport { createDataRouter } from './routes/data.js';\n\n// \u2500\u2500 Configuration \u2500\u2500\n\nconst __worker_dirname = dirname(fileURLToPath(import.meta.url));\nconst PORT = process.env.KIRO_MEMORY_WORKER_PORT || process.env.CONTEXTKIT_WORKER_PORT || 3001;\nconst HOST = process.env.KIRO_MEMORY_WORKER_HOST || process.env.CONTEXTKIT_WORKER_HOST || '127.0.0.1';\nconst PID_FILE = join(DATA_DIR, 'worker.pid');\nconst TOKEN_FILE = join(DATA_DIR, 'worker.token');\n\n// \u2500\u2500 Initialization \u2500\u2500\n\nif (!existsSync(DATA_DIR)) {\n mkdirSync(DATA_DIR, { recursive: true });\n}\n\n// Authentication token for hook \u2192 worker communication\nconst WORKER_TOKEN = crypto.randomBytes(32).toString('hex');\nwriteFileSync(TOKEN_FILE, WORKER_TOKEN, 'utf-8');\ntry {\n chmodSync(TOKEN_FILE, 0o600);\n} catch (err) {\n if (process.platform !== 'win32') {\n logger.warn('WORKER', `chmod 600 failed on ${TOKEN_FILE}`, {}, err as Error);\n }\n}\n\n// Database\nconst db = new KiroMemoryDatabase();\nlogger.info('WORKER', 'Database initialized');\n\n// Embedding service (lazy, non bloccante)\ngetHybridSearch().initialize().catch(err => {\n logger.warn('WORKER', 'Embedding initialization failed, FTS5 search only', {}, err as Error);\n});\n\n// Shared context for all routers\nconst ctx = createWorkerContext(db);\n\n// \u2500\u2500 Express app \u2500\u2500\n\nconst app = express();\n\n// Security: protective HTTP headers\napp.use(helmet({\n contentSecurityPolicy: {\n directives: {\n defaultSrc: [\"'self'\"],\n scriptSrc: [\"'self'\", \"'unsafe-inline'\"],\n styleSrc: [\"'self'\", \"'unsafe-inline'\", \"https://fonts.googleapis.com\"],\n imgSrc: [\"'self'\", \"data:\"],\n connectSrc: [\"'self'\"],\n fontSrc: [\"'self'\", \"https://fonts.gstatic.com\"],\n frameSrc: [\"'none'\"]\n }\n }\n}));\n\n// CORS restricted to localhost\napp.use(cors({\n origin: [\n `http://localhost:${PORT}`,\n `http://127.0.0.1:${PORT}`,\n `http://${HOST}:${PORT}`\n ],\n credentials: true,\n maxAge: 86400\n}));\n\n// Body size limit: 1MB\napp.use(express.json({ limit: '1mb' }));\n\n// Global rate limiting: 200 req/min per IP\napp.use('/api/', rateLimit({\n windowMs: 60_000,\n max: 200,\n standardHeaders: true,\n legacyHeaders: false,\n message: { error: 'Too many requests, retry later' }\n}));\n\n// \u2500\u2500 Mount modular routers \u2500\u2500\n\napp.use(createCoreRouter(ctx, WORKER_TOKEN));\napp.use(createObservationsRouter(ctx));\napp.use(createSummariesRouter(ctx));\napp.use(createSearchRouter(ctx));\napp.use(createAnalyticsRouter(ctx));\napp.use(createSessionsRouter(ctx));\napp.use(createProjectsRouter(ctx));\napp.use(createDataRouter(ctx, WORKER_TOKEN));\n\n// \u2500\u2500 Static files and viewer \u2500\u2500\n\napp.use(express.static(__worker_dirname, {\n index: false,\n maxAge: '1h'\n}));\n\napp.get('/', (_req, res) => {\n const viewerPath = join(__worker_dirname, 'viewer.html');\n if (existsSync(viewerPath)) {\n res.sendFile(viewerPath);\n } else {\n res.status(404).json({ error: 'Viewer not found. Run npm run build first.' });\n }\n});\n\n// \u2500\u2500 Server startup \u2500\u2500\n\nconst server = app.listen(Number(PORT), HOST, () => {\n logger.info('WORKER', `Kiro Memory worker started on http://${HOST}:${PORT}`);\n writeFileSync(PID_FILE, String(process.pid), 'utf-8');\n});\n\n// \u2500\u2500 Graceful shutdown \u2500\u2500\n\nfunction shutdown(signal: string): void {\n logger.info('WORKER', `Received ${signal}, shutting down...`);\n\n // Close all SSE clients to unblock server.close()\n const sseClients = getClients();\n for (const client of sseClients) {\n try { client.end(); } catch { /* ignora errori su client gi\u00E0 chiusi */ }\n }\n\n // Force timeout: if server.close() doesn't complete in 5s, force exit\n const forceTimeout = setTimeout(() => {\n logger.warn('WORKER', 'Forced shutdown after 5s timeout');\n if (existsSync(PID_FILE)) unlinkSync(PID_FILE);\n db.close();\n process.exit(1);\n }, 5000);\n\n server.close(() => {\n clearTimeout(forceTimeout);\n logger.info('WORKER', 'Server closed');\n\n if (existsSync(PID_FILE)) {\n unlinkSync(PID_FILE);\n }\n\n db.close();\n process.exit(0);\n });\n}\n\nprocess.on('SIGTERM', () => shutdown('SIGTERM'));\nprocess.on('SIGINT', () => shutdown('SIGINT'));\n\nprocess.on('uncaughtException', (error) => {\n logger.error('WORKER', 'Uncaught exception', {}, error);\n shutdown('uncaughtException');\n});\n\nprocess.on('unhandledRejection', (reason) => {\n logger.error('WORKER', 'Unhandled promise rejection', { reason }, reason as Error);\n});\n", "const dangerouslyDisableDefaultSrc = Symbol(\"dangerouslyDisableDefaultSrc\")\nconst SHOULD_BE_QUOTED = new Set([\"none\", \"self\", \"strict-dynamic\", \"report-sample\", \"inline-speculation-rules\", \"unsafe-inline\", \"unsafe-eval\", \"unsafe-hashes\", \"wasm-unsafe-eval\"])\nconst getDefaultDirectives = () => ({\n\t\"default-src\": [\"'self'\"],\n\t\"base-uri\": [\"'self'\"],\n\t\"font-src\": [\"'self'\", \"https:\", \"data:\"],\n\t\"form-action\": [\"'self'\"],\n\t\"frame-ancestors\": [\"'self'\"],\n\t\"img-src\": [\"'self'\", \"data:\"],\n\t\"object-src\": [\"'none'\"],\n\t\"script-src\": [\"'self'\"],\n\t\"script-src-attr\": [\"'none'\"],\n\t\"style-src\": [\"'self'\", \"https:\", \"'unsafe-inline'\"],\n\t\"upgrade-insecure-requests\": []\n})\nconst dashify = str => str.replace(/[A-Z]/g, capitalLetter => \"-\" + capitalLetter.toLowerCase())\nconst assertDirectiveValueIsValid = (directiveName, directiveValue) => {\n\tif (/;|,/.test(directiveValue)) {\n\t\tthrow new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}`)\n\t}\n}\nconst assertDirectiveValueEntryIsValid = (directiveName, directiveValueEntry) => {\n\tif (SHOULD_BE_QUOTED.has(directiveValueEntry) || directiveValueEntry.startsWith(\"nonce-\") || directiveValueEntry.startsWith(\"sha256-\") || directiveValueEntry.startsWith(\"sha384-\") || directiveValueEntry.startsWith(\"sha512-\")) {\n\t\tthrow new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}. ${JSON.stringify(directiveValueEntry)} should be quoted`)\n\t}\n}\nfunction normalizeDirectives(options) {\n\tconst defaultDirectives = getDefaultDirectives()\n\tconst {useDefaults = true, directives: rawDirectives = defaultDirectives} = options\n\tconst result = new Map()\n\tconst directiveNamesSeen = new Set()\n\tconst directivesExplicitlyDisabled = new Set()\n\tfor (const rawDirectiveName in rawDirectives) {\n\t\tif (!Object.hasOwn(rawDirectives, rawDirectiveName)) {\n\t\t\tcontinue\n\t\t}\n\t\tif (rawDirectiveName.length === 0 || /[^a-zA-Z0-9-]/.test(rawDirectiveName)) {\n\t\t\tthrow new Error(`Content-Security-Policy received an invalid directive name ${JSON.stringify(rawDirectiveName)}`)\n\t\t}\n\t\tconst directiveName = dashify(rawDirectiveName)\n\t\tif (directiveNamesSeen.has(directiveName)) {\n\t\t\tthrow new Error(`Content-Security-Policy received a duplicate directive ${JSON.stringify(directiveName)}`)\n\t\t}\n\t\tdirectiveNamesSeen.add(directiveName)\n\t\tconst rawDirectiveValue = rawDirectives[rawDirectiveName]\n\t\tlet directiveValue\n\t\tif (rawDirectiveValue === null) {\n\t\t\tif (directiveName === \"default-src\") {\n\t\t\t\tthrow new Error(\"Content-Security-Policy needs a default-src but it was set to `null`. If you really want to disable it, set it to `contentSecurityPolicy.dangerouslyDisableDefaultSrc`.\")\n\t\t\t}\n\t\t\tdirectivesExplicitlyDisabled.add(directiveName)\n\t\t\tcontinue\n\t\t} else if (typeof rawDirectiveValue === \"string\") {\n\t\t\tdirectiveValue = [rawDirectiveValue]\n\t\t} else if (!rawDirectiveValue) {\n\t\t\tthrow new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}`)\n\t\t} else if (rawDirectiveValue === dangerouslyDisableDefaultSrc) {\n\t\t\tif (directiveName === \"default-src\") {\n\t\t\t\tdirectivesExplicitlyDisabled.add(\"default-src\")\n\t\t\t\tcontinue\n\t\t\t} else {\n\t\t\t\tthrow new Error(`Content-Security-Policy: tried to disable ${JSON.stringify(directiveName)} as if it were default-src; simply omit the key`)\n\t\t\t}\n\t\t} else {\n\t\t\tdirectiveValue = rawDirectiveValue\n\t\t}\n\t\tfor (const element of directiveValue) {\n\t\t\tif (typeof element !== \"string\") continue\n\t\t\tassertDirectiveValueIsValid(directiveName, element)\n\t\t\tassertDirectiveValueEntryIsValid(directiveName, element)\n\t\t}\n\t\tresult.set(directiveName, directiveValue)\n\t}\n\tif (useDefaults) {\n\t\tObject.entries(defaultDirectives).forEach(([defaultDirectiveName, defaultDirectiveValue]) => {\n\t\t\tif (!result.has(defaultDirectiveName) && !directivesExplicitlyDisabled.has(defaultDirectiveName)) {\n\t\t\t\tresult.set(defaultDirectiveName, defaultDirectiveValue)\n\t\t\t}\n\t\t})\n\t}\n\tif (!result.size) {\n\t\tthrow new Error(\"Content-Security-Policy has no directives. Either set some or disable the header\")\n\t}\n\tif (!result.has(\"default-src\") && !directivesExplicitlyDisabled.has(\"default-src\")) {\n\t\tthrow new Error(\"Content-Security-Policy needs a default-src but none was provided. If you really want to disable it, set it to `contentSecurityPolicy.dangerouslyDisableDefaultSrc`.\")\n\t}\n\treturn result\n}\nfunction getHeaderValue(req, res, normalizedDirectives) {\n\tconst result = []\n\tfor (const [directiveName, rawDirectiveValue] of normalizedDirectives) {\n\t\tlet directiveValue = \"\"\n\t\tfor (const element of rawDirectiveValue) {\n\t\t\tif (typeof element === \"function\") {\n\t\t\t\tconst newElement = element(req, res)\n\t\t\t\tassertDirectiveValueEntryIsValid(directiveName, newElement)\n\t\t\t\tdirectiveValue += \" \" + newElement\n\t\t\t} else {\n\t\t\t\tdirectiveValue += \" \" + element\n\t\t\t}\n\t\t}\n\t\tif (directiveValue) {\n\t\t\tassertDirectiveValueIsValid(directiveName, directiveValue)\n\t\t\tresult.push(`${directiveName}${directiveValue}`)\n\t\t} else {\n\t\t\tresult.push(directiveName)\n\t\t}\n\t}\n\treturn result.join(\";\")\n}\nconst contentSecurityPolicy = function contentSecurityPolicy(options = {}) {\n\tconst headerName = options.reportOnly ? \"Content-Security-Policy-Report-Only\" : \"Content-Security-Policy\"\n\tconst normalizedDirectives = normalizeDirectives(options)\n\treturn function contentSecurityPolicyMiddleware(req, res, next) {\n\t\tconst result = getHeaderValue(req, res, normalizedDirectives)\n\t\tif (result instanceof Error) {\n\t\t\tnext(result)\n\t\t} else {\n\t\t\tres.setHeader(headerName, result)\n\t\t\tnext()\n\t\t}\n\t}\n}\ncontentSecurityPolicy.getDefaultDirectives = getDefaultDirectives\ncontentSecurityPolicy.dangerouslyDisableDefaultSrc = dangerouslyDisableDefaultSrc\n\nconst ALLOWED_POLICIES$2 = new Set([\"require-corp\", \"credentialless\", \"unsafe-none\"])\nfunction getHeaderValueFromOptions$6({policy = \"require-corp\"}) {\n\tif (ALLOWED_POLICIES$2.has(policy)) {\n\t\treturn policy\n\t} else {\n\t\tthrow new Error(`Cross-Origin-Embedder-Policy does not support the ${JSON.stringify(policy)} policy`)\n\t}\n}\nfunction crossOriginEmbedderPolicy(options = {}) {\n\tconst headerValue = getHeaderValueFromOptions$6(options)\n\treturn function crossOriginEmbedderPolicyMiddleware(_req, res, next) {\n\t\tres.setHeader(\"Cross-Origin-Embedder-Policy\", headerValue)\n\t\tnext()\n\t}\n}\n\nconst ALLOWED_POLICIES$1 = new Set([\"same-origin\", \"same-origin-allow-popups\", \"unsafe-none\"])\nfunction getHeaderValueFromOptions$5({policy = \"same-origin\"}) {\n\tif (ALLOWED_POLICIES$1.has(policy)) {\n\t\treturn policy\n\t} else {\n\t\tthrow new Error(`Cross-Origin-Opener-Policy does not support the ${JSON.stringify(policy)} policy`)\n\t}\n}\nfunction crossOriginOpenerPolicy(options = {}) {\n\tconst headerValue = getHeaderValueFromOptions$5(options)\n\treturn function crossOriginOpenerPolicyMiddleware(_req, res, next) {\n\t\tres.setHeader(\"Cross-Origin-Opener-Policy\", headerValue)\n\t\tnext()\n\t}\n}\n\nconst ALLOWED_POLICIES = new Set([\"same-origin\", \"same-site\", \"cross-origin\"])\nfunction getHeaderValueFromOptions$4({policy = \"same-origin\"}) {\n\tif (ALLOWED_POLICIES.has(policy)) {\n\t\treturn policy\n\t} else {\n\t\tthrow new Error(`Cross-Origin-Resource-Policy does not support the ${JSON.stringify(policy)} policy`)\n\t}\n}\nfunction crossOriginResourcePolicy(options = {}) {\n\tconst headerValue = getHeaderValueFromOptions$4(options)\n\treturn function crossOriginResourcePolicyMiddleware(_req, res, next) {\n\t\tres.setHeader(\"Cross-Origin-Resource-Policy\", headerValue)\n\t\tnext()\n\t}\n}\n\nfunction originAgentCluster() {\n\treturn function originAgentClusterMiddleware(_req, res, next) {\n\t\tres.setHeader(\"Origin-Agent-Cluster\", \"?1\")\n\t\tnext()\n\t}\n}\n\nconst ALLOWED_TOKENS = new Set([\"no-referrer\", \"no-referrer-when-downgrade\", \"same-origin\", \"origin\", \"strict-origin\", \"origin-when-cross-origin\", \"strict-origin-when-cross-origin\", \"unsafe-url\", \"\"])\nfunction getHeaderValueFromOptions$3({policy = [\"no-referrer\"]}) {\n\tconst tokens = typeof policy === \"string\" ? [policy] : policy\n\tif (tokens.length === 0) {\n\t\tthrow new Error(\"Referrer-Policy received no policy tokens\")\n\t}\n\tconst tokensSeen = new Set()\n\ttokens.forEach(token => {\n\t\tif (!ALLOWED_TOKENS.has(token)) {\n\t\t\tthrow new Error(`Referrer-Policy received an unexpected policy token ${JSON.stringify(token)}`)\n\t\t} else if (tokensSeen.has(token)) {\n\t\t\tthrow new Error(`Referrer-Policy received a duplicate policy token ${JSON.stringify(token)}`)\n\t\t}\n\t\ttokensSeen.add(token)\n\t})\n\treturn tokens.join(\",\")\n}\nfunction referrerPolicy(options = {}) {\n\tconst headerValue = getHeaderValueFromOptions$3(options)\n\treturn function referrerPolicyMiddleware(_req, res, next) {\n\t\tres.setHeader(\"Referrer-Policy\", headerValue)\n\t\tnext()\n\t}\n}\n\nconst DEFAULT_MAX_AGE = 365 * 24 * 60 * 60\nfunction parseMaxAge(value = DEFAULT_MAX_AGE) {\n\tif (value >= 0 && Number.isFinite(value)) {\n\t\treturn Math.floor(value)\n\t} else {\n\t\tthrow new Error(`Strict-Transport-Security: ${JSON.stringify(value)} is not a valid value for maxAge. Please choose a positive integer.`)\n\t}\n}\nfunction getHeaderValueFromOptions$2(options) {\n\tif (\"maxage\" in options) {\n\t\tthrow new Error(\"Strict-Transport-Security received an unsupported property, `maxage`. Did you mean to pass `maxAge`?\")\n\t}\n\tif (\"includeSubdomains\" in options) {\n\t\tthrow new Error('Strict-Transport-Security middleware should use `includeSubDomains` instead of `includeSubdomains`. (The correct one has an uppercase \"D\".)')\n\t}\n\tconst directives = [`max-age=${parseMaxAge(options.maxAge)}`]\n\tif (options.includeSubDomains === undefined || options.includeSubDomains) {\n\t\tdirectives.push(\"includeSubDomains\")\n\t}\n\tif (options.preload) {\n\t\tdirectives.push(\"preload\")\n\t}\n\treturn directives.join(\"; \")\n}\nfunction strictTransportSecurity(options = {}) {\n\tconst headerValue = getHeaderValueFromOptions$2(options)\n\treturn function strictTransportSecurityMiddleware(_req, res, next) {\n\t\tres.setHeader(\"Strict-Transport-Security\", headerValue)\n\t\tnext()\n\t}\n}\n\nfunction xContentTypeOptions() {\n\treturn function xContentTypeOptionsMiddleware(_req, res, next) {\n\t\tres.setHeader(\"X-Content-Type-Options\", \"nosniff\")\n\t\tnext()\n\t}\n}\n\nfunction xDnsPrefetchControl(options = {}) {\n\tconst headerValue = options.allow ? \"on\" : \"off\"\n\treturn function xDnsPrefetchControlMiddleware(_req, res, next) {\n\t\tres.setHeader(\"X-DNS-Prefetch-Control\", headerValue)\n\t\tnext()\n\t}\n}\n\nfunction xDownloadOptions() {\n\treturn function xDownloadOptionsMiddleware(_req, res, next) {\n\t\tres.setHeader(\"X-Download-Options\", \"noopen\")\n\t\tnext()\n\t}\n}\n\nfunction getHeaderValueFromOptions$1({action = \"sameorigin\"}) {\n\tconst normalizedAction = typeof action === \"string\" ? action.toUpperCase() : action\n\tswitch (normalizedAction) {\n\t\tcase \"SAME-ORIGIN\":\n\t\t\treturn \"SAMEORIGIN\"\n\t\tcase \"DENY\":\n\t\tcase \"SAMEORIGIN\":\n\t\t\treturn normalizedAction\n\t\tdefault:\n\t\t\tthrow new Error(`X-Frame-Options received an invalid action ${JSON.stringify(action)}`)\n\t}\n}\nfunction xFrameOptions(options = {}) {\n\tconst headerValue = getHeaderValueFromOptions$1(options)\n\treturn function xFrameOptionsMiddleware(_req, res, next) {\n\t\tres.setHeader(\"X-Frame-Options\", headerValue)\n\t\tnext()\n\t}\n}\n\nconst ALLOWED_PERMITTED_POLICIES = new Set([\"none\", \"master-only\", \"by-content-type\", \"all\"])\nfunction getHeaderValueFromOptions({permittedPolicies = \"none\"}) {\n\tif (ALLOWED_PERMITTED_POLICIES.has(permittedPolicies)) {\n\t\treturn permittedPolicies\n\t} else {\n\t\tthrow new Error(`X-Permitted-Cross-Domain-Policies does not support ${JSON.stringify(permittedPolicies)}`)\n\t}\n}\nfunction xPermittedCrossDomainPolicies(options = {}) {\n\tconst headerValue = getHeaderValueFromOptions(options)\n\treturn function xPermittedCrossDomainPoliciesMiddleware(_req, res, next) {\n\t\tres.setHeader(\"X-Permitted-Cross-Domain-Policies\", headerValue)\n\t\tnext()\n\t}\n}\n\nfunction xPoweredBy() {\n\treturn function xPoweredByMiddleware(_req, res, next) {\n\t\tres.removeHeader(\"X-Powered-By\")\n\t\tnext()\n\t}\n}\n\nfunction xXssProtection() {\n\treturn function xXssProtectionMiddleware(_req, res, next) {\n\t\tres.setHeader(\"X-XSS-Protection\", \"0\")\n\t\tnext()\n\t}\n}\n\nfunction getMiddlewareFunctionsFromOptions(options) {\n\tconst result = []\n\tswitch (options.contentSecurityPolicy) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(contentSecurityPolicy())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(contentSecurityPolicy(options.contentSecurityPolicy))\n\t\t\tbreak\n\t}\n\tswitch (options.crossOriginEmbedderPolicy) {\n\t\tcase undefined:\n\t\tcase false:\n\t\t\tbreak\n\t\tcase true:\n\t\t\tresult.push(crossOriginEmbedderPolicy())\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(crossOriginEmbedderPolicy(options.crossOriginEmbedderPolicy))\n\t\t\tbreak\n\t}\n\tswitch (options.crossOriginOpenerPolicy) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(crossOriginOpenerPolicy())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(crossOriginOpenerPolicy(options.crossOriginOpenerPolicy))\n\t\t\tbreak\n\t}\n\tswitch (options.crossOriginResourcePolicy) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(crossOriginResourcePolicy())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(crossOriginResourcePolicy(options.crossOriginResourcePolicy))\n\t\t\tbreak\n\t}\n\tswitch (options.originAgentCluster) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(originAgentCluster())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tconsole.warn(\"Origin-Agent-Cluster does not take options. Remove the property to silence this warning.\")\n\t\t\tresult.push(originAgentCluster())\n\t\t\tbreak\n\t}\n\tswitch (options.referrerPolicy) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(referrerPolicy())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(referrerPolicy(options.referrerPolicy))\n\t\t\tbreak\n\t}\n\tif (\"strictTransportSecurity\" in options && \"hsts\" in options) {\n\t\tthrow new Error(\"Strict-Transport-Security option was specified twice. Remove `hsts` to silence this warning.\")\n\t}\n\tconst strictTransportSecurityOption = options.strictTransportSecurity ?? options.hsts\n\tswitch (strictTransportSecurityOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(strictTransportSecurity())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(strictTransportSecurity(strictTransportSecurityOption))\n\t\t\tbreak\n\t}\n\tif (\"xContentTypeOptions\" in options && \"noSniff\" in options) {\n\t\tthrow new Error(\"X-Content-Type-Options option was specified twice. Remove `noSniff` to silence this warning.\")\n\t}\n\tconst xContentTypeOptionsOption = options.xContentTypeOptions ?? options.noSniff\n\tswitch (xContentTypeOptionsOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(xContentTypeOptions())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tconsole.warn(\"X-Content-Type-Options does not take options. Remove the property to silence this warning.\")\n\t\t\tresult.push(xContentTypeOptions())\n\t\t\tbreak\n\t}\n\tif (\"xDnsPrefetchControl\" in options && \"dnsPrefetchControl\" in options) {\n\t\tthrow new Error(\"X-DNS-Prefetch-Control option was specified twice. Remove `dnsPrefetchControl` to silence this warning.\")\n\t}\n\tconst xDnsPrefetchControlOption = options.xDnsPrefetchControl ?? options.dnsPrefetchControl\n\tswitch (xDnsPrefetchControlOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(xDnsPrefetchControl())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(xDnsPrefetchControl(xDnsPrefetchControlOption))\n\t\t\tbreak\n\t}\n\tif (\"xDownloadOptions\" in options && \"ieNoOpen\" in options) {\n\t\tthrow new Error(\"X-Download-Options option was specified twice. Remove `ieNoOpen` to silence this warning.\")\n\t}\n\tconst xDownloadOptionsOption = options.xDownloadOptions ?? options.ieNoOpen\n\tswitch (xDownloadOptionsOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(xDownloadOptions())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tconsole.warn(\"X-Download-Options does not take options. Remove the property to silence this warning.\")\n\t\t\tresult.push(xDownloadOptions())\n\t\t\tbreak\n\t}\n\tif (\"xFrameOptions\" in options && \"frameguard\" in options) {\n\t\tthrow new Error(\"X-Frame-Options option was specified twice. Remove `frameguard` to silence this warning.\")\n\t}\n\tconst xFrameOptionsOption = options.xFrameOptions ?? options.frameguard\n\tswitch (xFrameOptionsOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(xFrameOptions())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(xFrameOptions(xFrameOptionsOption))\n\t\t\tbreak\n\t}\n\tif (\"xPermittedCrossDomainPolicies\" in options && \"permittedCrossDomainPolicies\" in options) {\n\t\tthrow new Error(\"X-Permitted-Cross-Domain-Policies option was specified twice. Remove `permittedCrossDomainPolicies` to silence this warning.\")\n\t}\n\tconst xPermittedCrossDomainPoliciesOption = options.xPermittedCrossDomainPolicies ?? options.permittedCrossDomainPolicies\n\tswitch (xPermittedCrossDomainPoliciesOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(xPermittedCrossDomainPolicies())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(xPermittedCrossDomainPolicies(xPermittedCrossDomainPoliciesOption))\n\t\t\tbreak\n\t}\n\tif (\"xPoweredBy\" in options && \"hidePoweredBy\" in options) {\n\t\tthrow new Error(\"X-Powered-By option was specified twice. Remove `hidePoweredBy` to silence this warning.\")\n\t}\n\tconst xPoweredByOption = options.xPoweredBy ?? options.hidePoweredBy\n\tswitch (xPoweredByOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(xPoweredBy())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tconsole.warn(\"X-Powered-By does not take options. Remove the property to silence this warning.\")\n\t\t\tresult.push(xPoweredBy())\n\t\t\tbreak\n\t}\n\tif (\"xXssProtection\" in options && \"xssFilter\" in options) {\n\t\tthrow new Error(\"X-XSS-Protection option was specified twice. Remove `xssFilter` to silence this warning.\")\n\t}\n\tconst xXssProtectionOption = options.xXssProtection ?? options.xssFilter\n\tswitch (xXssProtectionOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(xXssProtection())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tconsole.warn(\"X-XSS-Protection does not take options. Remove the property to silence this warning.\")\n\t\t\tresult.push(xXssProtection())\n\t\t\tbreak\n\t}\n\treturn result\n}\nconst helmet = Object.assign(\n\tfunction helmet(options = {}) {\n\t\t// People should be able to pass an options object with no prototype,\n\t\t// so we want this optional chaining.\n\t\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n\t\tif (options.constructor?.name === \"IncomingMessage\") {\n\t\t\tthrow new Error(\"It appears you have done something like `app.use(helmet)`, but it should be `app.use(helmet())`.\")\n\t\t}\n\t\tconst middlewareFunctions = getMiddlewareFunctionsFromOptions(options)\n\t\treturn function helmetMiddleware(req, res, next) {\n\t\t\tlet middlewareIndex = 0\n\t\t\t;(function internalNext(err) {\n\t\t\t\tif (err) {\n\t\t\t\t\tnext(err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst middlewareFunction = middlewareFunctions[middlewareIndex]\n\t\t\t\tif (middlewareFunction) {\n\t\t\t\t\tmiddlewareIndex++\n\t\t\t\t\tmiddlewareFunction(req, res, internalNext)\n\t\t\t\t} else {\n\t\t\t\t\tnext()\n\t\t\t\t}\n\t\t\t})()\n\t\t}\n\t},\n\t{\n\t\tcontentSecurityPolicy,\n\t\tcrossOriginEmbedderPolicy,\n\t\tcrossOriginOpenerPolicy,\n\t\tcrossOriginResourcePolicy,\n\t\toriginAgentCluster,\n\t\treferrerPolicy,\n\t\tstrictTransportSecurity,\n\t\txContentTypeOptions,\n\t\txDnsPrefetchControl,\n\t\txDownloadOptions,\n\t\txFrameOptions,\n\t\txPermittedCrossDomainPolicies,\n\t\txPoweredBy,\n\t\txXssProtection,\n\t\t// Legacy aliases\n\t\tdnsPrefetchControl: xDnsPrefetchControl,\n\t\txssFilter: xXssProtection,\n\t\tpermittedCrossDomainPolicies: xPermittedCrossDomainPolicies,\n\t\tieNoOpen: xDownloadOptions,\n\t\tnoSniff: xContentTypeOptions,\n\t\tframeguard: xFrameOptions,\n\t\thidePoweredBy: xPoweredBy,\n\t\thsts: strictTransportSecurity\n\t}\n)\n\nexport {contentSecurityPolicy, crossOriginEmbedderPolicy, crossOriginOpenerPolicy, crossOriginResourcePolicy, helmet as default, xDnsPrefetchControl as dnsPrefetchControl, xFrameOptions as frameguard, xPoweredBy as hidePoweredBy, strictTransportSecurity as hsts, xDownloadOptions as ieNoOpen, xContentTypeOptions as noSniff, originAgentCluster, xPermittedCrossDomainPolicies as permittedCrossDomainPolicies, referrerPolicy, strictTransportSecurity, xContentTypeOptions, xDnsPrefetchControl, xDownloadOptions, xFrameOptions, xPermittedCrossDomainPolicies, xPoweredBy, xXssProtection, xXssProtection as xssFilter}\n", "// source/ip-key-generator.ts\nimport { isIPv6 } from \"node:net\";\nimport { Address6 } from \"ip-address\";\nfunction ipKeyGenerator(ip, ipv6Subnet = 56) {\n if (ipv6Subnet && isIPv6(ip)) {\n return `${new Address6(`${ip}/${ipv6Subnet}`).startAddress().correctForm()}/${ipv6Subnet}`;\n }\n return ip;\n}\n\n// source/memory-store.ts\nvar MemoryStore = class {\n constructor(validations2) {\n this.validations = validations2;\n /**\n * These two maps store usage (requests) and reset time by key (for example, IP\n * addresses or API keys).\n *\n * They are split into two to avoid having to iterate through the entire set to\n * determine which ones need reset. Instead, `Client`s are moved from `previous`\n * to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients\n * left in `previous`, i.e., those that have not made any recent requests, are\n * known to be expired and can be deleted in bulk.\n */\n this.previous = /* @__PURE__ */ new Map();\n this.current = /* @__PURE__ */ new Map();\n /**\n * Confirmation that the keys incremented in once instance of MemoryStore\n * cannot affect other instances.\n */\n this.localKeys = true;\n }\n /**\n * Method that initializes the store.\n *\n * @param options {Options} - The options used to setup the middleware.\n */\n init(options) {\n this.windowMs = options.windowMs;\n this.validations?.windowMs(this.windowMs);\n if (this.interval) clearInterval(this.interval);\n this.interval = setInterval(() => {\n this.clearExpired();\n }, this.windowMs);\n this.interval.unref?.();\n }\n /**\n * Method to fetch a client's hit count and reset time.\n *\n * @param key {string} - The identifier for a client.\n *\n * @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.\n *\n * @public\n */\n async get(key) {\n return this.current.get(key) ?? this.previous.get(key);\n }\n /**\n * Method to increment a client's hit counter.\n *\n * @param key {string} - The identifier for a client.\n *\n * @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.\n *\n * @public\n */\n async increment(key) {\n const client = this.getClient(key);\n const now = Date.now();\n if (client.resetTime.getTime() <= now) {\n this.resetClient(client, now);\n }\n client.totalHits++;\n return client;\n }\n /**\n * Method to decrement a client's hit counter.\n *\n * @param key {string} - The identifier for a client.\n *\n * @public\n */\n async decrement(key) {\n const client = this.getClient(key);\n if (client.totalHits > 0) client.totalHits--;\n }\n /**\n * Method to reset a client's hit counter.\n *\n * @param key {string} - The identifier for a client.\n *\n * @public\n */\n async resetKey(key) {\n this.current.delete(key);\n this.previous.delete(key);\n }\n /**\n * Method to reset everyone's hit counter.\n *\n * @public\n */\n async resetAll() {\n this.current.clear();\n this.previous.clear();\n }\n /**\n * Method to stop the timer (if currently running) and prevent any memory\n * leaks.\n *\n * @public\n */\n shutdown() {\n clearInterval(this.interval);\n void this.resetAll();\n }\n /**\n * Recycles a client by setting its hit count to zero, and reset time to\n * `windowMs` milliseconds from now.\n *\n * NOT to be confused with `#resetKey()`, which removes a client from both the\n * `current` and `previous` maps.\n *\n * @param client {Client} - The client to recycle.\n * @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.\n *\n * @return {Client} - The modified client that was passed in, to allow for chaining.\n */\n resetClient(client, now = Date.now()) {\n client.totalHits = 0;\n client.resetTime.setTime(now + this.windowMs);\n return client;\n }\n /**\n * Retrieves or creates a client, given a key. Also ensures that the client being\n * returned is in the `current` map.\n *\n * @param key {string} - The key under which the client is (or is to be) stored.\n *\n * @returns {Client} - The requested client.\n */\n getClient(key) {\n if (this.current.has(key)) return this.current.get(key);\n let client;\n if (this.previous.has(key)) {\n client = this.previous.get(key);\n this.previous.delete(key);\n } else {\n client = { totalHits: 0, resetTime: /* @__PURE__ */ new Date() };\n this.resetClient(client);\n }\n this.current.set(key, client);\n return client;\n }\n /**\n * Move current clients to previous, create a new map for current.\n *\n * This function is called every `windowMs`.\n */\n clearExpired() {\n this.previous = this.current;\n this.current = /* @__PURE__ */ new Map();\n }\n};\n\n// source/rate-limit.ts\nimport { isIPv6 as isIPv62 } from \"node:net\";\n\n// source/headers.ts\nimport { Buffer } from \"node:buffer\";\nimport { createHash } from \"node:crypto\";\nvar SUPPORTED_DRAFT_VERSIONS = [\n \"draft-6\",\n \"draft-7\",\n \"draft-8\"\n];\nvar getResetSeconds = (windowMs, resetTime) => {\n let resetSeconds;\n if (resetTime) {\n const deltaSeconds = Math.ceil((resetTime.getTime() - Date.now()) / 1e3);\n resetSeconds = Math.max(0, deltaSeconds);\n } else {\n resetSeconds = Math.ceil(windowMs / 1e3);\n }\n return resetSeconds;\n};\nvar getPartitionKey = (key) => {\n const hash = createHash(\"sha256\");\n hash.update(key);\n const partitionKey = hash.digest(\"hex\").slice(0, 12);\n return Buffer.from(partitionKey).toString(\"base64\");\n};\nvar setLegacyHeaders = (response, info) => {\n if (response.headersSent) return;\n response.setHeader(\"X-RateLimit-Limit\", info.limit.toString());\n response.setHeader(\"X-RateLimit-Remaining\", info.remaining.toString());\n if (info.resetTime instanceof Date) {\n response.setHeader(\"Date\", (/* @__PURE__ */ new Date()).toUTCString());\n response.setHeader(\n \"X-RateLimit-Reset\",\n Math.ceil(info.resetTime.getTime() / 1e3).toString()\n );\n }\n};\nvar setDraft6Headers = (response, info, windowMs) => {\n if (response.headersSent) return;\n const windowSeconds = Math.ceil(windowMs / 1e3);\n const resetSeconds = getResetSeconds(windowMs, info.resetTime);\n response.setHeader(\"RateLimit-Policy\", `${info.limit};w=${windowSeconds}`);\n response.setHeader(\"RateLimit-Limit\", info.limit.toString());\n response.setHeader(\"RateLimit-Remaining\", info.remaining.toString());\n if (typeof resetSeconds === \"number\")\n response.setHeader(\"RateLimit-Reset\", resetSeconds.toString());\n};\nvar setDraft7Headers = (response, info, windowMs) => {\n if (response.headersSent) return;\n const windowSeconds = Math.ceil(windowMs / 1e3);\n const resetSeconds = getResetSeconds(windowMs, info.resetTime);\n response.setHeader(\"RateLimit-Policy\", `${info.limit};w=${windowSeconds}`);\n response.setHeader(\n \"RateLimit\",\n `limit=${info.limit}, remaining=${info.remaining}, reset=${resetSeconds}`\n );\n};\nvar setDraft8Headers = (response, info, windowMs, name, key) => {\n if (response.headersSent) return;\n const windowSeconds = Math.ceil(windowMs / 1e3);\n const resetSeconds = getResetSeconds(windowMs, info.resetTime);\n const partitionKey = getPartitionKey(key);\n const header = `r=${info.remaining}; t=${resetSeconds}`;\n const policy = `q=${info.limit}; w=${windowSeconds}; pk=:${partitionKey}:`;\n response.append(\"RateLimit\", `\"${name}\"; ${header}`);\n response.append(\"RateLimit-Policy\", `\"${name}\"; ${policy}`);\n};\nvar setRetryAfterHeader = (response, info, windowMs) => {\n if (response.headersSent) return;\n const resetSeconds = getResetSeconds(windowMs, info.resetTime);\n response.setHeader(\"Retry-After\", resetSeconds.toString());\n};\n\n// source/utils.ts\nvar omitUndefinedProperties = (passedOptions) => {\n const omittedOptions = {};\n for (const k of Object.keys(passedOptions)) {\n const key = k;\n if (passedOptions[key] !== void 0) {\n omittedOptions[key] = passedOptions[key];\n }\n }\n return omittedOptions;\n};\n\n// source/validations.ts\nimport { isIP } from \"node:net\";\nvar ValidationError = class extends Error {\n /**\n * The code must be a string, in snake case and all capital, that starts with\n * the substring `ERR_ERL_`.\n *\n * The message must be a string, starting with an uppercase character,\n * describing the issue in detail.\n */\n constructor(code, message) {\n const url = `https://express-rate-limit.github.io/${code}/`;\n super(`${message} See ${url} for more information.`);\n this.name = this.constructor.name;\n this.code = code;\n this.help = url;\n }\n};\nvar ChangeWarning = class extends ValidationError {\n};\nvar usedStores = /* @__PURE__ */ new Set();\nvar singleCountKeys = /* @__PURE__ */ new WeakMap();\nvar validations = {\n enabled: {\n default: true\n },\n // Should be EnabledValidations type, but that's a circular reference\n disable() {\n for (const k of Object.keys(this.enabled)) this.enabled[k] = false;\n },\n /**\n * Checks whether the IP address is valid, and that it does not have a port\n * number in it.\n *\n * See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_invalid_ip_address.\n *\n * @param ip {string | undefined} - The IP address provided by Express as request.ip.\n *\n * @returns {void}\n */\n ip(ip) {\n if (ip === void 0) {\n throw new ValidationError(\n \"ERR_ERL_UNDEFINED_IP_ADDRESS\",\n `An undefined 'request.ip' was detected. This might indicate a misconfiguration or the connection being destroyed prematurely.`\n );\n }\n if (!isIP(ip)) {\n throw new ValidationError(\n \"ERR_ERL_INVALID_IP_ADDRESS\",\n `An invalid 'request.ip' (${ip}) was detected. Consider passing a custom 'keyGenerator' function to the rate limiter.`\n );\n }\n },\n /**\n * Makes sure the trust proxy setting is not set to `true`.\n *\n * See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_permissive_trust_proxy.\n *\n * @param request {Request} - The Express request object.\n *\n * @returns {void}\n */\n trustProxy(request) {\n if (request.app.get(\"trust proxy\") === true) {\n throw new ValidationError(\n \"ERR_ERL_PERMISSIVE_TRUST_PROXY\",\n `The Express 'trust proxy' setting is true, which allows anyone to trivially bypass IP-based rate limiting.`\n );\n }\n },\n /**\n * Makes sure the trust proxy setting is set in case the `X-Forwarded-For`\n * header is present.\n *\n * See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_unset_trust_proxy.\n *\n * @param request {Request} - The Express request object.\n *\n * @returns {void}\n */\n xForwardedForHeader(request) {\n if (request.headers[\"x-forwarded-for\"] && request.app.get(\"trust proxy\") === false) {\n throw new ValidationError(\n \"ERR_ERL_UNEXPECTED_X_FORWARDED_FOR\",\n `The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default). This could indicate a misconfiguration which would prevent express-rate-limit from accurately identifying users.`\n );\n }\n },\n /**\n * Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)\n *\n * @param request {Request} - The Express request object.\n *\n * @returns {void}\n */\n forwardedHeader(request) {\n if (request.headers.forwarded && request.ip === request.socket?.remoteAddress) {\n throw new ValidationError(\n \"ERR_ERL_FORWARDED_HEADER\",\n `The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.`\n );\n }\n },\n /**\n * Ensures totalHits value from store is a positive integer.\n *\n * @param hits {any} - The `totalHits` returned by the store.\n */\n positiveHits(hits) {\n if (typeof hits !== \"number\" || hits < 1 || hits !== Math.round(hits)) {\n throw new ValidationError(\n \"ERR_ERL_INVALID_HITS\",\n `The totalHits value returned from the store must be a positive integer, got ${hits}`\n );\n }\n },\n /**\n * Ensures a single store instance is not used with multiple express-rate-limit instances\n */\n unsharedStore(store) {\n if (usedStores.has(store)) {\n const maybeUniquePrefix = store?.localKeys ? \"\" : \" (with a unique prefix)\";\n throw new ValidationError(\n \"ERR_ERL_STORE_REUSE\",\n `A Store instance must not be shared across multiple rate limiters. Create a new instance of ${store.constructor.name}${maybeUniquePrefix} for each limiter instead.`\n );\n }\n usedStores.add(store);\n },\n /**\n * Ensures a given key is incremented only once per request.\n *\n * @param request {Request} - The Express request object.\n * @param store {Store} - The store class.\n * @param key {string} - The key used to store the client's hit count.\n *\n * @returns {void}\n */\n singleCount(request, store, key) {\n let storeKeys = singleCountKeys.get(request);\n if (!storeKeys) {\n storeKeys = /* @__PURE__ */ new Map();\n singleCountKeys.set(request, storeKeys);\n }\n const storeKey = store.localKeys ? store : store.constructor.name;\n let keys = storeKeys.get(storeKey);\n if (!keys) {\n keys = [];\n storeKeys.set(storeKey, keys);\n }\n const prefixedKey = `${store.prefix ?? \"\"}${key}`;\n if (keys.includes(prefixedKey)) {\n throw new ValidationError(\n \"ERR_ERL_DOUBLE_COUNT\",\n `The hit count for ${key} was incremented more than once for a single request.`\n );\n }\n keys.push(prefixedKey);\n },\n /**\n * Warns the user that the behaviour for `max: 0` / `limit: 0` is\n * changing in the next major release.\n *\n * @param limit {number} - The maximum number of hits per client.\n *\n * @returns {void}\n */\n limit(limit) {\n if (limit === 0) {\n throw new ChangeWarning(\n \"WRN_ERL_MAX_ZERO\",\n \"Setting limit or max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7\"\n );\n }\n },\n /**\n * Warns the user that the `draft_polli_ratelimit_headers` option is deprecated\n * and will be removed in the next major release.\n *\n * @param draft_polli_ratelimit_headers {any | undefined} - The now-deprecated setting that was used to enable standard headers.\n *\n * @returns {void}\n */\n draftPolliHeaders(draft_polli_ratelimit_headers) {\n if (draft_polli_ratelimit_headers) {\n throw new ChangeWarning(\n \"WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS\",\n `The draft_polli_ratelimit_headers configuration option is deprecated and has been removed in express-rate-limit v7, please set standardHeaders: 'draft-6' instead.`\n );\n }\n },\n /**\n * Warns the user that the `onLimitReached` option is deprecated and\n * will be removed in the next major release.\n *\n * @param onLimitReached {any | undefined} - The maximum number of hits per client.\n *\n * @returns {void}\n */\n onLimitReached(onLimitReached) {\n if (onLimitReached) {\n throw new ChangeWarning(\n \"WRN_ERL_DEPRECATED_ON_LIMIT_REACHED\",\n \"The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7.\"\n );\n }\n },\n /**\n * Warns the user when an invalid/unsupported version of the draft spec is passed.\n *\n * @param version {any | undefined} - The version passed by the user.\n *\n * @returns {void}\n */\n headersDraftVersion(version) {\n if (typeof version !== \"string\" || // @ts-expect-error This is fine. If version is not in the array, it will just return false.\n !SUPPORTED_DRAFT_VERSIONS.includes(version)) {\n const versionString = SUPPORTED_DRAFT_VERSIONS.join(\", \");\n throw new ValidationError(\n \"ERR_ERL_HEADERS_UNSUPPORTED_DRAFT_VERSION\",\n `standardHeaders: only the following versions of the IETF draft specification are supported: ${versionString}.`\n );\n }\n },\n /**\n * Warns the user when the selected headers option requires a reset time but\n * the store does not provide one.\n *\n * @param resetTime {Date | undefined} - The timestamp when the client's hit count will be reset.\n *\n * @returns {void}\n */\n headersResetTime(resetTime) {\n if (!resetTime) {\n throw new ValidationError(\n \"ERR_ERL_HEADERS_NO_RESET\",\n `standardHeaders: 'draft-7' requires a 'resetTime', but the store did not provide one. The 'windowMs' value will be used instead, which may cause clients to wait longer than necessary.`\n );\n }\n },\n knownOptions(passedOptions) {\n if (!passedOptions) return;\n const optionsMap = {\n windowMs: true,\n limit: true,\n message: true,\n statusCode: true,\n legacyHeaders: true,\n standardHeaders: true,\n identifier: true,\n requestPropertyName: true,\n skipFailedRequests: true,\n skipSuccessfulRequests: true,\n keyGenerator: true,\n ipv6Subnet: true,\n handler: true,\n skip: true,\n requestWasSuccessful: true,\n store: true,\n validate: true,\n headers: true,\n max: true,\n passOnStoreError: true\n };\n const validOptions = Object.keys(optionsMap).concat(\n \"draft_polli_ratelimit_headers\",\n // not a valid option anymore, but we have a more specific check for this one, so don't warn for it here\n // from express-slow-down - https://github.com/express-rate-limit/express-slow-down/blob/main/source/types.ts#L65\n \"delayAfter\",\n \"delayMs\",\n \"maxDelayMs\"\n );\n for (const key of Object.keys(passedOptions)) {\n if (!validOptions.includes(key)) {\n throw new ValidationError(\n \"ERR_ERL_UNKNOWN_OPTION\",\n `Unexpected configuration option: ${key}`\n // todo: suggest a valid option with a short levenstein distance?\n );\n }\n }\n },\n /**\n * Checks the options.validate setting to ensure that only recognized\n * validations are enabled or disabled.\n *\n * If any unrecognized values are found, an error is logged that\n * includes the list of supported validations.\n */\n validationsConfig() {\n const supportedValidations = Object.keys(this).filter(\n (k) => ![\"enabled\", \"disable\"].includes(k)\n );\n supportedValidations.push(\"default\");\n for (const key of Object.keys(this.enabled)) {\n if (!supportedValidations.includes(key)) {\n throw new ValidationError(\n \"ERR_ERL_UNKNOWN_VALIDATION\",\n `options.validate.${key} is not recognized. Supported validate options are: ${supportedValidations.join(\n \", \"\n )}.`\n );\n }\n }\n },\n /**\n * Checks to see if the instance was created inside of a request handler,\n * which would prevent it from working correctly, with the default memory\n * store (or any other store with localKeys.)\n */\n creationStack(store) {\n const { stack } = new Error(\n \"express-rate-limit validation check (set options.validate.creationStack=false to disable)\"\n );\n if (stack?.includes(\"Layer.handle [as handle_request]\") || // express v4\n stack?.includes(\"Layer.handleRequest\")) {\n if (!store.localKeys) {\n throw new ValidationError(\n \"ERR_ERL_CREATED_IN_REQUEST_HANDLER\",\n \"express-rate-limit instance should *usually* be created at app initialization, not when responding to a request.\"\n );\n }\n throw new ValidationError(\n \"ERR_ERL_CREATED_IN_REQUEST_HANDLER\",\n \"express-rate-limit instance should be created at app initialization, not when responding to a request.\"\n );\n }\n },\n ipv6Subnet(ipv6Subnet) {\n if (ipv6Subnet === false) {\n return;\n }\n if (!Number.isInteger(ipv6Subnet) || ipv6Subnet < 32 || ipv6Subnet > 64) {\n throw new ValidationError(\n \"ERR_ERL_IPV6_SUBNET\",\n `Unexpected ipv6Subnet value: ${ipv6Subnet}. Expected an integer between 32 and 64 (usually 48-64).`\n );\n }\n },\n ipv6SubnetOrKeyGenerator(options) {\n if (options.ipv6Subnet !== void 0 && options.keyGenerator) {\n throw new ValidationError(\n \"ERR_ERL_IPV6SUBNET_OR_KEYGENERATOR\",\n `Incompatible options: the 'ipv6Subnet' option is ignored when a custom 'keyGenerator' function is also set.`\n );\n }\n },\n keyGeneratorIpFallback(keyGenerator) {\n if (!keyGenerator) {\n return;\n }\n const src = keyGenerator.toString();\n if ((src.includes(\"req.ip\") || src.includes(\"request.ip\")) && !src.includes(\"ipKeyGenerator\")) {\n throw new ValidationError(\n \"ERR_ERL_KEY_GEN_IPV6\",\n \"Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits.\"\n );\n }\n },\n /**\n * Checks to see if the window duration is greater than 2^32 - 1. This is only\n * called by the default MemoryStore, since it uses Node's setInterval method.\n *\n * See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.\n */\n windowMs(windowMs) {\n const SET_TIMEOUT_MAX = 2 ** 31 - 1;\n if (typeof windowMs !== \"number\" || Number.isNaN(windowMs) || windowMs < 1 || windowMs > SET_TIMEOUT_MAX) {\n throw new ValidationError(\n \"ERR_ERL_WINDOW_MS\",\n `Invalid windowMs value: ${windowMs}${typeof windowMs !== \"number\" ? ` (${typeof windowMs})` : \"\"}, must be a number between 1 and ${SET_TIMEOUT_MAX} when using the default MemoryStore`\n );\n }\n }\n};\nvar getValidations = (_enabled) => {\n let enabled;\n if (typeof _enabled === \"boolean\") {\n enabled = {\n default: _enabled\n };\n } else {\n enabled = {\n default: true,\n ..._enabled\n };\n }\n const wrappedValidations = { enabled };\n for (const [name, validation] of Object.entries(validations)) {\n if (typeof validation === \"function\")\n wrappedValidations[name] = (...args) => {\n if (!(enabled[name] ?? enabled.default)) {\n return;\n }\n try {\n ;\n validation.apply(\n wrappedValidations,\n args\n );\n } catch (error) {\n if (error instanceof ChangeWarning) console.warn(error);\n else console.error(error);\n }\n };\n }\n return wrappedValidations;\n};\n\n// source/rate-limit.ts\nvar isLegacyStore = (store) => (\n // Check that `incr` exists but `increment` does not - store authors might want\n // to keep both around for backwards compatibility.\n typeof store.incr === \"function\" && typeof store.increment !== \"function\"\n);\nvar promisifyStore = (passedStore) => {\n if (!isLegacyStore(passedStore)) {\n return passedStore;\n }\n const legacyStore = passedStore;\n class PromisifiedStore {\n async increment(key) {\n return new Promise((resolve, reject) => {\n legacyStore.incr(\n key,\n (error, totalHits, resetTime) => {\n if (error) reject(error);\n resolve({ totalHits, resetTime });\n }\n );\n });\n }\n async decrement(key) {\n return legacyStore.decrement(key);\n }\n async resetKey(key) {\n return legacyStore.resetKey(key);\n }\n /* istanbul ignore next */\n async resetAll() {\n if (typeof legacyStore.resetAll === \"function\")\n return legacyStore.resetAll();\n }\n }\n return new PromisifiedStore();\n};\nvar getOptionsFromConfig = (config) => {\n const { validations: validations2, ...directlyPassableEntries } = config;\n return {\n ...directlyPassableEntries,\n validate: validations2.enabled\n };\n};\nvar parseOptions = (passedOptions) => {\n const notUndefinedOptions = omitUndefinedProperties(passedOptions);\n const validations2 = getValidations(notUndefinedOptions?.validate ?? true);\n validations2.validationsConfig();\n validations2.knownOptions(passedOptions);\n validations2.draftPolliHeaders(\n // @ts-expect-error see the note above.\n notUndefinedOptions.draft_polli_ratelimit_headers\n );\n validations2.onLimitReached(notUndefinedOptions.onLimitReached);\n if (notUndefinedOptions.ipv6Subnet !== void 0 && typeof notUndefinedOptions.ipv6Subnet !== \"function\") {\n validations2.ipv6Subnet(notUndefinedOptions.ipv6Subnet);\n }\n validations2.keyGeneratorIpFallback(notUndefinedOptions.keyGenerator);\n validations2.ipv6SubnetOrKeyGenerator(notUndefinedOptions);\n let standardHeaders = notUndefinedOptions.standardHeaders ?? false;\n if (standardHeaders === true) standardHeaders = \"draft-6\";\n const config = {\n windowMs: 60 * 1e3,\n limit: passedOptions.max ?? 5,\n // `max` is deprecated, but support it anyways.\n message: \"Too many requests, please try again later.\",\n statusCode: 429,\n legacyHeaders: passedOptions.headers ?? true,\n identifier(request, _response) {\n let duration = \"\";\n const property = config.requestPropertyName;\n const { limit } = request[property];\n const seconds = config.windowMs / 1e3;\n const minutes = config.windowMs / (1e3 * 60);\n const hours = config.windowMs / (1e3 * 60 * 60);\n const days = config.windowMs / (1e3 * 60 * 60 * 24);\n if (seconds < 60) duration = `${seconds}sec`;\n else if (minutes < 60) duration = `${minutes}min`;\n else if (hours < 24) duration = `${hours}hr${hours > 1 ? \"s\" : \"\"}`;\n else duration = `${days}day${days > 1 ? \"s\" : \"\"}`;\n return `${limit}-in-${duration}`;\n },\n requestPropertyName: \"rateLimit\",\n skipFailedRequests: false,\n skipSuccessfulRequests: false,\n requestWasSuccessful: (_request, response) => response.statusCode < 400,\n skip: (_request, _response) => false,\n async keyGenerator(request, response) {\n validations2.ip(request.ip);\n validations2.trustProxy(request);\n validations2.xForwardedForHeader(request);\n validations2.forwardedHeader(request);\n const ip = request.ip;\n let subnet = 56;\n if (isIPv62(ip)) {\n subnet = typeof config.ipv6Subnet === \"function\" ? await config.ipv6Subnet(request, response) : config.ipv6Subnet;\n if (typeof config.ipv6Subnet === \"function\")\n validations2.ipv6Subnet(subnet);\n }\n return ipKeyGenerator(ip, subnet);\n },\n ipv6Subnet: 56,\n async handler(request, response, _next, _optionsUsed) {\n response.status(config.statusCode);\n const message = typeof config.message === \"function\" ? await config.message(\n request,\n response\n ) : config.message;\n if (!response.writableEnded) response.send(message);\n },\n passOnStoreError: false,\n // Allow the default options to be overridden by the passed options.\n ...notUndefinedOptions,\n // `standardHeaders` is resolved into a draft version above, use that.\n standardHeaders,\n // Note that this field is declared after the user's options are spread in,\n // so that this field doesn't get overridden with an un-promisified store!\n store: promisifyStore(\n notUndefinedOptions.store ?? new MemoryStore(validations2)\n ),\n // Print an error to the console if a few known misconfigurations are detected.\n validations: validations2\n };\n if (typeof config.store.increment !== \"function\" || typeof config.store.decrement !== \"function\" || typeof config.store.resetKey !== \"function\" || config.store.resetAll !== void 0 && typeof config.store.resetAll !== \"function\" || config.store.init !== void 0 && typeof config.store.init !== \"function\") {\n throw new TypeError(\n \"An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface.\"\n );\n }\n return config;\n};\nvar handleAsyncErrors = (fn) => async (request, response, next) => {\n try {\n await Promise.resolve(fn(request, response, next)).catch(next);\n } catch (error) {\n next(error);\n }\n};\nvar rateLimit = (passedOptions) => {\n const config = parseOptions(passedOptions ?? {});\n const options = getOptionsFromConfig(config);\n config.validations.creationStack(config.store);\n config.validations.unsharedStore(config.store);\n if (typeof config.store.init === \"function\") config.store.init(options);\n const middleware = handleAsyncErrors(\n async (request, response, next) => {\n const skip = await config.skip(request, response);\n if (skip) {\n next();\n return;\n }\n const augmentedRequest = request;\n const key = await config.keyGenerator(request, response);\n let totalHits = 0;\n let resetTime;\n try {\n const incrementResult = await config.store.increment(key);\n totalHits = incrementResult.totalHits;\n resetTime = incrementResult.resetTime;\n } catch (error) {\n if (config.passOnStoreError) {\n console.error(\n \"express-rate-limit: error from store, allowing request without rate-limiting.\",\n error\n );\n next();\n return;\n }\n throw error;\n }\n config.validations.positiveHits(totalHits);\n config.validations.singleCount(request, config.store, key);\n const retrieveLimit = typeof config.limit === \"function\" ? config.limit(request, response) : config.limit;\n const limit = await retrieveLimit;\n config.validations.limit(limit);\n const info = {\n limit,\n used: totalHits,\n remaining: Math.max(limit - totalHits, 0),\n resetTime,\n key\n };\n Object.defineProperty(info, \"current\", {\n configurable: false,\n enumerable: false,\n value: totalHits\n });\n augmentedRequest[config.requestPropertyName] = info;\n if (config.legacyHeaders && !response.headersSent) {\n setLegacyHeaders(response, info);\n }\n if (config.standardHeaders && !response.headersSent) {\n switch (config.standardHeaders) {\n case \"draft-6\": {\n setDraft6Headers(response, info, config.windowMs);\n break;\n }\n case \"draft-7\": {\n config.validations.headersResetTime(info.resetTime);\n setDraft7Headers(response, info, config.windowMs);\n break;\n }\n case \"draft-8\": {\n const retrieveName = typeof config.identifier === \"function\" ? config.identifier(request, response) : config.identifier;\n const name = await retrieveName;\n config.validations.headersResetTime(info.resetTime);\n setDraft8Headers(response, info, config.windowMs, name, key);\n break;\n }\n default: {\n config.validations.headersDraftVersion(config.standardHeaders);\n break;\n }\n }\n }\n if (config.skipFailedRequests || config.skipSuccessfulRequests) {\n let decremented = false;\n const decrementKey = async () => {\n if (!decremented) {\n await config.store.decrement(key);\n decremented = true;\n }\n };\n if (config.skipFailedRequests) {\n response.on(\"finish\", async () => {\n if (!await config.requestWasSuccessful(request, response))\n await decrementKey();\n });\n response.on(\"close\", async () => {\n if (!response.writableEnded) await decrementKey();\n });\n response.on(\"error\", async () => {\n await decrementKey();\n });\n }\n if (config.skipSuccessfulRequests) {\n response.on(\"finish\", async () => {\n if (await config.requestWasSuccessful(request, response))\n await decrementKey();\n });\n }\n }\n config.validations.disable();\n if (totalHits > limit) {\n if (config.legacyHeaders || config.standardHeaders) {\n setRetryAfterHeader(response, info, config.windowMs);\n }\n config.handler(request, response, next, options);\n return;\n }\n next();\n }\n );\n const getThrowFn = () => {\n throw new Error(\"The current store does not support the get/getKey method\");\n };\n middleware.resetKey = config.store.resetKey.bind(config.store);\n middleware.getKey = typeof config.store.get === \"function\" ? config.store.get.bind(config.store) : getThrowFn;\n return middleware;\n};\nvar rate_limit_default = rateLimit;\nexport {\n MemoryStore,\n rate_limit_default as default,\n ipKeyGenerator,\n rate_limit_default as rateLimit\n};\n", "/**\n * Shim bun:sqlite \u2192 better-sqlite3\n *\n * Provides a bun:sqlite-compatible API using better-sqlite3\n * to allow execution on plain Node.js.\n */\n\nimport BetterSqlite3 from 'better-sqlite3';\n\n/**\n * bun:sqlite-compatible Database class\n */\nexport class Database {\n private _db: BetterSqlite3.Database;\n private _stmtCache: Map<string, BunQueryCompat> = new Map();\n\n constructor(path: string, options?: { create?: boolean; readwrite?: boolean }) {\n this._db = new BetterSqlite3(path, {\n // better-sqlite3 creates the file by default ('create' not needed)\n readonly: options?.readwrite === false ? true : false\n });\n }\n\n /**\n * Execute a SQL query without results\n */\n run(sql: string, params?: any[]): { lastInsertRowid: number | bigint; changes: number } {\n const stmt = this._db.prepare(sql);\n const result = params ? stmt.run(...params) : stmt.run();\n return result;\n }\n\n /**\n * Prepare a query with bun:sqlite-compatible interface.\n * Returns a cached prepared statement for repeated queries.\n */\n query(sql: string): BunQueryCompat {\n let cached = this._stmtCache.get(sql);\n if (!cached) {\n cached = new BunQueryCompat(this._db, sql);\n this._stmtCache.set(sql, cached);\n }\n return cached;\n }\n\n /**\n * Create a transaction\n */\n transaction<T>(fn: (...args: any[]) => T): (...args: any[]) => T {\n return this._db.transaction(fn) as any;\n }\n\n /**\n * Close the connection\n */\n close(): void {\n this._stmtCache.clear();\n this._db.close();\n }\n}\n\n/**\n * Query wrapper compatible with the bun:sqlite Statement API.\n * Prepares the statement once at construction time (cached).\n */\nclass BunQueryCompat {\n private _stmt: BetterSqlite3.Statement;\n\n constructor(db: BetterSqlite3.Database, sql: string) {\n this._stmt = db.prepare(sql);\n }\n\n /**\n * Returns all rows\n */\n all(...params: any[]): any[] {\n return params.length > 0 ? this._stmt.all(...params) : this._stmt.all();\n }\n\n /**\n * Returns the first row or null\n */\n get(...params: any[]): any {\n return params.length > 0 ? this._stmt.get(...params) : this._stmt.get();\n }\n\n /**\n * Execute without results\n */\n run(...params: any[]): { lastInsertRowid: number | bigint; changes: number } {\n return params.length > 0 ? this._stmt.run(...params) : this._stmt.run();\n }\n}\n\nexport default { Database };\n", "import { join, dirname, basename } from 'path';\nimport { homedir } from 'os';\nimport { existsSync, mkdirSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { fileURLToPath } from 'url';\nimport { logger } from '../utils/logger.js';\n\n// Get __dirname that works in both ESM and CJS contexts\nfunction getDirname(): string {\n if (typeof __dirname !== 'undefined') {\n return __dirname;\n }\n return dirname(fileURLToPath(import.meta.url));\n}\n\nconst _dirname = getDirname();\n\n/**\n * Simple path configuration for Kiro Memory\n */\n\n// Base directory - Kiro Memory data in home directory\n// Backward compat: if ~/.contextkit (old name) exists, use it; otherwise ~/.kiro-memory\nconst _legacyDir = join(homedir(), '.contextkit');\nconst _defaultDir = existsSync(_legacyDir) ? _legacyDir : join(homedir(), '.kiro-memory');\nexport const DATA_DIR = process.env.KIRO_MEMORY_DATA_DIR || process.env.CONTEXTKIT_DATA_DIR || _defaultDir;\n\n// Kiro config directory\nexport const KIRO_CONFIG_DIR = process.env.KIRO_CONFIG_DIR || join(homedir(), '.kiro');\n\n// Plugin installation directory\nexport const PLUGIN_ROOT = join(KIRO_CONFIG_DIR, 'plugins', 'kiro-memory');\n\n// Data subdirectories\nexport const ARCHIVES_DIR = join(DATA_DIR, 'archives');\nexport const LOGS_DIR = join(DATA_DIR, 'logs');\nexport const TRASH_DIR = join(DATA_DIR, 'trash');\nexport const BACKUPS_DIR = join(DATA_DIR, 'backups');\nexport const MODES_DIR = join(DATA_DIR, 'modes');\nexport const USER_SETTINGS_PATH = join(DATA_DIR, 'settings.json');\n// Backward compat: if the DB with the old name exists, use it\nconst _legacyDb = join(DATA_DIR, 'contextkit.db');\nexport const DB_PATH = existsSync(_legacyDb) ? _legacyDb : join(DATA_DIR, 'kiro-memory.db');\nexport const VECTOR_DB_DIR = join(DATA_DIR, 'vector-db');\n\n// Observer sessions directory\nexport const OBSERVER_SESSIONS_DIR = join(DATA_DIR, 'observer-sessions');\n\n// Kiro integration paths\nexport const KIRO_SETTINGS_PATH = join(KIRO_CONFIG_DIR, 'settings.json');\nexport const KIRO_CONTEXT_PATH = join(KIRO_CONFIG_DIR, 'context.md');\n\n/**\n * Get project-specific archive directory\n */\nexport function getProjectArchiveDir(projectName: string): string {\n return join(ARCHIVES_DIR, projectName);\n}\n\n/**\n * Get worker socket path for a session\n */\nexport function getWorkerSocketPath(sessionId: number): string {\n return join(DATA_DIR, `worker-${sessionId}.sock`);\n}\n\n/**\n * Ensure a directory exists\n */\nexport function ensureDir(dirPath: string): void {\n mkdirSync(dirPath, { recursive: true });\n}\n\n/**\n * Ensure all data directories exist\n */\nexport function ensureAllDataDirs(): void {\n ensureDir(DATA_DIR);\n ensureDir(ARCHIVES_DIR);\n ensureDir(LOGS_DIR);\n ensureDir(TRASH_DIR);\n ensureDir(BACKUPS_DIR);\n ensureDir(MODES_DIR);\n}\n\n/**\n * Ensure modes directory exists\n */\nexport function ensureModesDir(): void {\n ensureDir(MODES_DIR);\n}\n\n/**\n * Get current project name from git root or cwd\n */\nexport function getCurrentProjectName(): string {\n try {\n const gitRoot = execSync('git rev-parse --show-toplevel', {\n cwd: process.cwd(),\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'ignore'],\n windowsHide: true\n }).trim();\n return basename(gitRoot);\n } catch (error) {\n logger.debug('SYSTEM', 'Git root detection failed, using cwd basename', {\n cwd: process.cwd()\n }, error as Error);\n return basename(process.cwd());\n }\n}\n\n/**\n * Find package root directory\n */\nexport function getPackageRoot(): string {\n return join(_dirname, '..');\n}\n\n/**\n * Create a timestamped backup filename\n */\nexport function createBackupFilename(originalPath: string): string {\n const timestamp = new Date()\n .toISOString()\n .replace(/[:.]/g, '-')\n .replace('T', '_')\n .slice(0, 19);\n\n return `${originalPath}.backup.${timestamp}`;\n}\n", "/**\n * Structured Logger for Kiro Memory Worker Service\n * Provides readable, traceable logging with correlation IDs and data flow tracking\n */\n\nimport { appendFileSync, existsSync, mkdirSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\n\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n SILENT = 4\n}\n\nexport type Component = 'HOOK' | 'WORKER' | 'SDK' | 'PARSER' | 'DB' | 'SYSTEM' | 'HTTP' | 'SESSION' | 'CHROMA' | 'CHROMA_SYNC' | 'FOLDER_INDEX' | 'CONTEXT' | 'QUEUE' | 'EMBEDDING' | 'SEARCH' | 'VECTOR';\n\ninterface LogContext {\n sessionId?: number;\n memorySessionId?: string;\n correlationId?: string;\n [key: string]: any;\n}\n\n// Default data directory for Kiro Memory\nconst DEFAULT_DATA_DIR = join(homedir(), '.contextkit');\n\nclass Logger {\n private level: LogLevel | null = null;\n private useColor: boolean;\n private logFilePath: string | null = null;\n private logFileInitialized: boolean = false;\n\n constructor() {\n // Disable colors when output is not a TTY (e.g., PM2 logs)\n this.useColor = process.stdout.isTTY ?? false;\n }\n\n /**\n * Initialize log file path and ensure directory exists (lazy initialization)\n */\n private ensureLogFileInitialized(): void {\n if (this.logFileInitialized) return;\n this.logFileInitialized = true;\n\n try {\n const logsDir = join(DEFAULT_DATA_DIR, 'logs');\n\n // Ensure logs directory exists\n if (!existsSync(logsDir)) {\n mkdirSync(logsDir, { recursive: true });\n }\n\n // Create log file path with date\n const date = new Date().toISOString().split('T')[0];\n this.logFilePath = join(logsDir, `kiro-memory-${date}.log`);\n } catch (error) {\n console.error('[LOGGER] Failed to initialize log file:', error);\n this.logFilePath = null;\n }\n }\n\n /**\n * Lazy-load log level from settings file\n */\n private getLevel(): LogLevel {\n if (this.level === null) {\n try {\n const settingsPath = join(DEFAULT_DATA_DIR, 'settings.json');\n if (existsSync(settingsPath)) {\n const settingsData = readFileSync(settingsPath, 'utf-8');\n const settings = JSON.parse(settingsData);\n const envLevel = (settings.KIRO_MEMORY_LOG_LEVEL || settings.CONTEXTKIT_LOG_LEVEL || 'INFO').toUpperCase();\n this.level = LogLevel[envLevel as keyof typeof LogLevel] ?? LogLevel.INFO;\n } else {\n this.level = LogLevel.INFO;\n }\n } catch (error) {\n this.level = LogLevel.INFO;\n }\n }\n return this.level;\n }\n\n /**\n * Create correlation ID for tracking an observation through the pipeline\n */\n correlationId(sessionId: number, observationNum: number): string {\n return `obs-${sessionId}-${observationNum}`;\n }\n\n /**\n * Create session correlation ID\n */\n sessionId(sessionId: number): string {\n return `session-${sessionId}`;\n }\n\n /**\n * Format data for logging - create compact summaries instead of full dumps\n */\n private formatData(data: any): string {\n if (data === null || data === undefined) return '';\n if (typeof data === 'string') return data;\n if (typeof data === 'number') return data.toString();\n if (typeof data === 'boolean') return data.toString();\n\n if (typeof data === 'object') {\n if (data instanceof Error) {\n return this.getLevel() === LogLevel.DEBUG\n ? `${data.message}\\n${data.stack}`\n : data.message;\n }\n\n if (Array.isArray(data)) {\n return `[${data.length} items]`;\n }\n\n const keys = Object.keys(data);\n if (keys.length === 0) return '{}';\n if (keys.length <= 3) {\n return JSON.stringify(data);\n }\n return `{${keys.length} keys: ${keys.slice(0, 3).join(', ')}...}`;\n }\n\n return String(data);\n }\n\n /**\n * Format timestamp in local timezone (YYYY-MM-DD HH:MM:SS.mmm)\n */\n private formatTimestamp(date: Date): string {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const day = String(date.getDate()).padStart(2, '0');\n const hours = String(date.getHours()).padStart(2, '0');\n const minutes = String(date.getMinutes()).padStart(2, '0');\n const seconds = String(date.getSeconds()).padStart(2, '0');\n const ms = String(date.getMilliseconds()).padStart(3, '0');\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${ms}`;\n }\n\n /**\n * Core logging method\n */\n private log(\n level: LogLevel,\n component: Component,\n message: string,\n context?: LogContext,\n data?: any\n ): void {\n if (level < this.getLevel()) return;\n\n this.ensureLogFileInitialized();\n\n const timestamp = this.formatTimestamp(new Date());\n const levelStr = LogLevel[level].padEnd(5);\n const componentStr = component.padEnd(6);\n\n let correlationStr = '';\n if (context?.correlationId) {\n correlationStr = `[${context.correlationId}] `;\n } else if (context?.sessionId) {\n correlationStr = `[session-${context.sessionId}] `;\n }\n\n let dataStr = '';\n if (data !== undefined && data !== null) {\n if (data instanceof Error) {\n dataStr = this.getLevel() === LogLevel.DEBUG\n ? `\\n${data.message}\\n${data.stack}`\n : ` ${data.message}`;\n } else if (this.getLevel() === LogLevel.DEBUG && typeof data === 'object') {\n dataStr = '\\n' + JSON.stringify(data, null, 2);\n } else {\n dataStr = ' ' + this.formatData(data);\n }\n }\n\n let contextStr = '';\n if (context) {\n const { sessionId, memorySessionId, correlationId, ...rest } = context;\n if (Object.keys(rest).length > 0) {\n const pairs = Object.entries(rest).map(([k, v]) => `${k}=${v}`);\n contextStr = ` {${pairs.join(', ')}}`;\n }\n }\n\n const logLine = `[${timestamp}] [${levelStr}] [${componentStr}] ${correlationStr}${message}${contextStr}${dataStr}`;\n\n if (this.logFilePath) {\n try {\n appendFileSync(this.logFilePath, logLine + '\\n', 'utf8');\n } catch (error) {\n process.stderr.write(`[LOGGER] Failed to write to log file: ${error}\\n`);\n }\n } else {\n process.stderr.write(logLine + '\\n');\n }\n }\n\n // Public logging methods\n debug(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.DEBUG, component, message, context, data);\n }\n\n info(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.INFO, component, message, context, data);\n }\n\n warn(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.WARN, component, message, context, data);\n }\n\n error(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.ERROR, component, message, context, data);\n }\n\n /**\n * Log data flow: input \u2192 processing\n */\n dataIn(component: Component, message: string, context?: LogContext, data?: any): void {\n this.info(component, `\u2192 ${message}`, context, data);\n }\n\n /**\n * Log data flow: processing \u2192 output\n */\n dataOut(component: Component, message: string, context?: LogContext, data?: any): void {\n this.info(component, `\u2190 ${message}`, context, data);\n }\n\n /**\n * Log successful completion\n */\n success(component: Component, message: string, context?: LogContext, data?: any): void {\n this.info(component, `\u2713 ${message}`, context, data);\n }\n\n /**\n * Log failure\n */\n failure(component: Component, message: string, context?: LogContext, data?: any): void {\n this.error(component, `\u2717 ${message}`, context, data);\n }\n\n /**\n * Log timing information\n */\n timing(component: Component, message: string, durationMs: number, context?: LogContext): void {\n this.info(component, `\u23F1 ${message}`, context, { duration: `${durationMs}ms` });\n }\n\n /**\n * Happy Path Error - logs when the expected \"happy path\" fails but we have a fallback\n */\n happyPathError<T = string>(\n component: Component,\n message: string,\n context?: LogContext,\n data?: any,\n fallback: T = '' as T\n ): T {\n const stack = new Error().stack || '';\n const stackLines = stack.split('\\n');\n const callerLine = stackLines[2] || '';\n const callerMatch = callerLine.match(/at\\s+(?:.*\\s+)?\\(?([^:]+):(\\d+):(\\d+)\\)?/);\n const location = callerMatch\n ? `${callerMatch[1].split('/').pop()}:${callerMatch[2]}`\n : 'unknown';\n\n const enhancedContext = {\n ...context,\n location\n };\n\n this.warn(component, `[HAPPY-PATH] ${message}`, enhancedContext, data);\n\n return fallback;\n }\n}\n\n// Export singleton instance\nexport const logger = new Logger();\n", "import { Database } from 'bun:sqlite';\nimport { DATA_DIR, DB_PATH, ensureDir } from '../../shared/paths.js';\nimport { logger } from '../../utils/logger.js';\n\n// SQLite configuration constants\nconst SQLITE_MMAP_SIZE_BYTES = 256 * 1024 * 1024; // 256MB\nconst SQLITE_CACHE_SIZE_PAGES = 10_000;\n\nexport interface Migration {\n version: number;\n up: (db: Database) => void;\n down?: (db: Database) => void;\n}\n\n\n\n/**\n * KiroMemoryDatabase - Main entry point for the sqlite module\n *\n * Sets up bun:sqlite with optimized settings and runs all migrations.\n *\n * Usage:\n * const db = new KiroMemoryDatabase(); // uses default DB_PATH\n * const db = new KiroMemoryDatabase('/path/to/db.sqlite');\n * const db = new KiroMemoryDatabase(':memory:'); // for tests\n */\nexport class KiroMemoryDatabase {\n private _db: Database;\n\n /**\n * Readonly accessor for the underlying Database instance.\n * Prefer using query() and run() proxy methods directly.\n */\n get db(): Database {\n return this._db;\n }\n\n /**\n * @param dbPath - Path to the SQLite file (default: DB_PATH)\n * @param skipMigrations - If true, skip the migration runner (for high-frequency hooks)\n */\n constructor(dbPath: string = DB_PATH, skipMigrations: boolean = false) {\n // Ensure data directory exists (skip for in-memory databases)\n if (dbPath !== ':memory:') {\n ensureDir(DATA_DIR);\n }\n\n // Create database connection\n this._db = new Database(dbPath, { create: true, readwrite: true });\n\n // Apply optimized SQLite settings\n this._db.run('PRAGMA journal_mode = WAL');\n this._db.run('PRAGMA busy_timeout = 5000'); // Wait up to 5s on concurrent lock (hook + worker)\n this._db.run('PRAGMA synchronous = NORMAL');\n this._db.run('PRAGMA foreign_keys = ON');\n this._db.run('PRAGMA temp_store = memory');\n this._db.run(`PRAGMA mmap_size = ${SQLITE_MMAP_SIZE_BYTES}`);\n this._db.run(`PRAGMA cache_size = ${SQLITE_CACHE_SIZE_PAGES}`);\n\n // Run migrations only if needed (hooks skip them for performance)\n if (!skipMigrations) {\n const migrationRunner = new MigrationRunner(this._db);\n migrationRunner.runAllMigrations();\n }\n }\n\n /**\n * Prepare a query (delegates to underlying Database).\n * Proxy method to avoid ctx.db.db.query() double access.\n */\n query(sql: string) {\n return this._db.query(sql);\n }\n\n /**\n * Execute a SQL statement without results (delegates to underlying Database).\n * Proxy method to avoid ctx.db.db.run() double access.\n */\n run(sql: string, params?: any[]) {\n return this._db.run(sql, params);\n }\n\n /**\n * Executes a function within an atomic transaction.\n * If fn() throws an error, the transaction is automatically rolled back.\n */\n withTransaction<T>(fn: (db: Database) => T): T {\n const transaction = this._db.transaction(fn);\n return transaction(this._db);\n }\n\n /**\n * Close the database connection\n */\n close(): void {\n this._db.close();\n }\n}\n\n/**\n * Migration runner for Kiro Memory\n */\nclass MigrationRunner {\n private db: Database;\n\n constructor(db: Database) {\n this.db = db;\n }\n\n runAllMigrations(): void {\n // Create schema_versions table if not exists\n this.db.run(`\n CREATE TABLE IF NOT EXISTS schema_versions (\n id INTEGER PRIMARY KEY,\n version INTEGER UNIQUE NOT NULL,\n applied_at TEXT NOT NULL\n )\n `);\n\n // Get current version\n const versionQuery = this.db.query('SELECT MAX(version) as version FROM schema_versions');\n const result = versionQuery.get() as { version: number } | null;\n const currentVersion = result?.version || 0;\n\n // Run migrations\n const migrations = this.getMigrations();\n for (const migration of migrations) {\n if (migration.version > currentVersion) {\n logger.info('DB', `Applying migration ${migration.version}`);\n \n const transaction = this.db.transaction(() => {\n migration.up(this.db);\n const insert = this.db.query('INSERT INTO schema_versions (version, applied_at) VALUES (?, ?)');\n insert.run(migration.version, new Date().toISOString());\n });\n \n transaction();\n logger.info('DB', `Migration ${migration.version} applied successfully`);\n }\n }\n }\n\n private getMigrations(): Migration[] {\n return [\n {\n version: 1,\n up: (db) => {\n // Sessions table\n db.run(`\n CREATE TABLE IF NOT EXISTS sessions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n content_session_id TEXT NOT NULL UNIQUE,\n project TEXT NOT NULL,\n user_prompt TEXT NOT NULL,\n memory_session_id TEXT,\n status TEXT DEFAULT 'active',\n started_at TEXT NOT NULL,\n started_at_epoch INTEGER NOT NULL,\n completed_at TEXT,\n completed_at_epoch INTEGER\n )\n `);\n\n // Observations table\n db.run(`\n CREATE TABLE IF NOT EXISTS observations (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n memory_session_id TEXT NOT NULL,\n project TEXT NOT NULL,\n type TEXT NOT NULL,\n title TEXT NOT NULL,\n subtitle TEXT,\n text TEXT,\n narrative TEXT,\n facts TEXT,\n concepts TEXT,\n files_read TEXT,\n files_modified TEXT,\n prompt_number INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n created_at_epoch INTEGER NOT NULL\n )\n `);\n\n // Summaries table\n db.run(`\n CREATE TABLE IF NOT EXISTS summaries (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL,\n project TEXT NOT NULL,\n request TEXT,\n investigated TEXT,\n learned TEXT,\n completed TEXT,\n next_steps TEXT,\n notes TEXT,\n created_at TEXT NOT NULL,\n created_at_epoch INTEGER NOT NULL\n )\n `);\n\n // Prompts table\n db.run(`\n CREATE TABLE IF NOT EXISTS prompts (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n content_session_id TEXT NOT NULL,\n project TEXT NOT NULL,\n prompt_number INTEGER NOT NULL,\n prompt_text TEXT NOT NULL,\n created_at TEXT NOT NULL,\n created_at_epoch INTEGER NOT NULL\n )\n `);\n\n // Pending messages table\n db.run(`\n CREATE TABLE IF NOT EXISTS pending_messages (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n content_session_id TEXT NOT NULL,\n type TEXT NOT NULL,\n data TEXT NOT NULL,\n created_at TEXT NOT NULL,\n created_at_epoch INTEGER NOT NULL\n )\n `);\n\n // Indexes\n db.run('CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project)');\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_project ON observations(project)');\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_session ON observations(memory_session_id)');\n db.run('CREATE INDEX IF NOT EXISTS idx_summaries_session ON summaries(session_id)');\n db.run('CREATE INDEX IF NOT EXISTS idx_prompts_session ON prompts(content_session_id)');\n }\n },\n {\n version: 2,\n up: (db) => {\n // FTS5 table for full-text search on observations\n db.run(`\n CREATE VIRTUAL TABLE IF NOT EXISTS observations_fts USING fts5(\n title, text, narrative, concepts,\n content='observations',\n content_rowid='id'\n )\n `);\n\n // Triggers to keep FTS5 synchronized\n db.run(`\n CREATE TRIGGER IF NOT EXISTS observations_ai AFTER INSERT ON observations BEGIN\n INSERT INTO observations_fts(rowid, title, text, narrative, concepts)\n VALUES (new.id, new.title, new.text, new.narrative, new.concepts);\n END\n `);\n\n db.run(`\n CREATE TRIGGER IF NOT EXISTS observations_ad AFTER DELETE ON observations BEGIN\n INSERT INTO observations_fts(observations_fts, rowid, title, text, narrative, concepts)\n VALUES ('delete', old.id, old.title, old.text, old.narrative, old.concepts);\n END\n `);\n\n db.run(`\n CREATE TRIGGER IF NOT EXISTS observations_au AFTER UPDATE ON observations BEGIN\n INSERT INTO observations_fts(observations_fts, rowid, title, text, narrative, concepts)\n VALUES ('delete', old.id, old.title, old.text, old.narrative, old.concepts);\n INSERT INTO observations_fts(rowid, title, text, narrative, concepts)\n VALUES (new.id, new.title, new.text, new.narrative, new.concepts);\n END\n `);\n\n // Backfill existing observations into the FTS5 table\n db.run(`\n INSERT INTO observations_fts(rowid, title, text, narrative, concepts)\n SELECT id, title, text, narrative, concepts FROM observations\n `);\n\n // Additional indexes for search performance\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_type ON observations(type)');\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_epoch ON observations(created_at_epoch)');\n db.run('CREATE INDEX IF NOT EXISTS idx_summaries_project ON summaries(project)');\n db.run('CREATE INDEX IF NOT EXISTS idx_summaries_epoch ON summaries(created_at_epoch)');\n db.run('CREATE INDEX IF NOT EXISTS idx_prompts_project ON prompts(project)');\n }\n },\n {\n version: 3,\n up: (db) => {\n // Alias table for renaming projects in the UI\n db.run(`\n CREATE TABLE IF NOT EXISTS project_aliases (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_name TEXT NOT NULL UNIQUE,\n display_name TEXT NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n )\n `);\n\n db.run('CREATE UNIQUE INDEX IF NOT EXISTS idx_project_aliases_name ON project_aliases(project_name)');\n }\n },\n {\n version: 4,\n up: (db) => {\n // Embeddings table for local semantic search\n db.run(`\n CREATE TABLE IF NOT EXISTS observation_embeddings (\n observation_id INTEGER PRIMARY KEY,\n embedding BLOB NOT NULL,\n model TEXT NOT NULL,\n dimensions INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n FOREIGN KEY (observation_id) REFERENCES observations(id) ON DELETE CASCADE\n )\n `);\n\n db.run('CREATE INDEX IF NOT EXISTS idx_embeddings_model ON observation_embeddings(model)');\n }\n },\n {\n version: 5,\n up: (db) => {\n // Track last access (search that found the observation)\n db.run('ALTER TABLE observations ADD COLUMN last_accessed_epoch INTEGER');\n // Stale flag: 0 = fresh, 1 = file modified after the observation\n db.run('ALTER TABLE observations ADD COLUMN is_stale INTEGER DEFAULT 0');\n // Index for decay queries\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_last_accessed ON observations(last_accessed_epoch)');\n // Index for stale queries\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_stale ON observations(is_stale)');\n }\n },\n {\n version: 6,\n up: (db) => {\n // Checkpoint table for session resume\n db.run(`\n CREATE TABLE IF NOT EXISTS checkpoints (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id INTEGER NOT NULL,\n project TEXT NOT NULL,\n task TEXT NOT NULL,\n progress TEXT,\n next_steps TEXT,\n open_questions TEXT,\n relevant_files TEXT,\n context_snapshot TEXT,\n created_at TEXT NOT NULL,\n created_at_epoch INTEGER NOT NULL,\n FOREIGN KEY (session_id) REFERENCES sessions(id)\n )\n `);\n db.run('CREATE INDEX IF NOT EXISTS idx_checkpoints_session ON checkpoints(session_id)');\n db.run('CREATE INDEX IF NOT EXISTS idx_checkpoints_project ON checkpoints(project)');\n db.run('CREATE INDEX IF NOT EXISTS idx_checkpoints_epoch ON checkpoints(created_at_epoch)');\n }\n },\n {\n version: 7,\n up: (db) => {\n // Content hash for content-based deduplication (SHA256)\n db.run('ALTER TABLE observations ADD COLUMN content_hash TEXT');\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_hash ON observations(content_hash)');\n }\n },\n {\n version: 8,\n up: (db) => {\n // Token economics: tokens spent to generate the observation (discovery cost)\n db.run('ALTER TABLE observations ADD COLUMN discovery_tokens INTEGER DEFAULT 0');\n // Token economics on summaries\n db.run('ALTER TABLE summaries ADD COLUMN discovery_tokens INTEGER DEFAULT 0');\n }\n },\n {\n version: 9,\n up: (db) => {\n // Composite indexes for pagination and project filters\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_project_epoch ON observations(project, created_at_epoch DESC)');\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_project_type ON observations(project, type)');\n db.run('CREATE INDEX IF NOT EXISTS idx_summaries_project_epoch ON summaries(project, created_at_epoch DESC)');\n db.run('CREATE INDEX IF NOT EXISTS idx_prompts_project_epoch ON prompts(project, created_at_epoch DESC)');\n }\n }\n ];\n }\n}\n\n// Re-export bun:sqlite Database type\nexport { Database };\n", "/**\n * Local embedding service for Kiro Memory\n *\n * Provider: fastembed (primary) \u2192 @huggingface/transformers (fallback) \u2192 null (FTS5 only)\n * Generates 384-dim vector embeddings for semantic search.\n * Lazy loading: the model is loaded only on first use.\n */\n\nimport { logger } from '../../utils/logger.js';\n\ntype EmbeddingProvider = 'fastembed' | 'transformers' | null;\n\nexport class EmbeddingService {\n private provider: EmbeddingProvider = null;\n private model: any = null;\n private initialized = false;\n private initializing: Promise<boolean> | null = null;\n\n /**\n * Initialize the embedding service.\n * Tries fastembed, then @huggingface/transformers, then fallback to null.\n */\n async initialize(): Promise<boolean> {\n if (this.initialized) return this.provider !== null;\n\n // Avoid concurrent initializations\n if (this.initializing) return this.initializing;\n\n this.initializing = this._doInitialize();\n const result = await this.initializing;\n this.initializing = null;\n return result;\n }\n\n private async _doInitialize(): Promise<boolean> {\n // Attempt 1: fastembed\n try {\n const fastembed = await import('fastembed');\n const EmbeddingModel = fastembed.EmbeddingModel || fastembed.default?.EmbeddingModel;\n const FlagEmbedding = fastembed.FlagEmbedding || fastembed.default?.FlagEmbedding;\n\n if (FlagEmbedding && EmbeddingModel) {\n this.model = await FlagEmbedding.init({\n model: EmbeddingModel.BGESmallENV15\n });\n this.provider = 'fastembed';\n this.initialized = true;\n logger.info('EMBEDDING', 'Initialized with fastembed (BGE-small-en-v1.5)');\n return true;\n }\n } catch (error) {\n logger.debug('EMBEDDING', `fastembed not available: ${error}`);\n }\n\n // Attempt 2: @huggingface/transformers\n try {\n const transformers = await import('@huggingface/transformers');\n const pipeline = (transformers as any).pipeline || (transformers as any).default?.pipeline;\n\n if (pipeline) {\n this.model = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2', {\n quantized: true\n } as any);\n this.provider = 'transformers';\n this.initialized = true;\n logger.info('EMBEDDING', 'Initialized with @huggingface/transformers (all-MiniLM-L6-v2)');\n return true;\n }\n } catch (error) {\n logger.debug('EMBEDDING', `@huggingface/transformers not available: ${error}`);\n }\n\n // No provider available\n this.provider = null;\n this.initialized = true;\n logger.warn('EMBEDDING', 'No embedding provider available, semantic search disabled');\n return false;\n }\n\n /**\n * Generate embedding for a single text.\n * Returns Float32Array with 384 dimensions, or null if not available.\n */\n async embed(text: string): Promise<Float32Array | null> {\n if (!this.initialized) await this.initialize();\n if (!this.provider || !this.model) return null;\n\n try {\n // Truncate text that is too long (max ~512 tokens \u2248 2000 chars)\n const truncated = text.substring(0, 2000);\n\n if (this.provider === 'fastembed') {\n return await this._embedFastembed(truncated);\n } else if (this.provider === 'transformers') {\n return await this._embedTransformers(truncated);\n }\n } catch (error) {\n logger.error('EMBEDDING', `Error generating embedding: ${error}`);\n }\n\n return null;\n }\n\n /**\n * Generate embeddings in batch.\n * Uses native batch support when available (fastembed, transformers),\n * falls back to serial processing on batch failure.\n */\n async embedBatch(texts: string[]): Promise<(Float32Array | null)[]> {\n if (!this.initialized) await this.initialize();\n if (!this.provider || !this.model) return texts.map(() => null);\n if (texts.length === 0) return [];\n\n // Truncate all texts upfront (max ~512 tokens \u2248 2000 chars)\n const truncated = texts.map(t => t.substring(0, 2000));\n\n // Try native batch embedding first\n try {\n if (this.provider === 'fastembed') {\n return await this._embedBatchFastembed(truncated);\n } else if (this.provider === 'transformers') {\n return await this._embedBatchTransformers(truncated);\n }\n } catch (error) {\n logger.warn('EMBEDDING', `Batch embedding failed, falling back to serial: ${error}`);\n }\n\n // Serial fallback: process one at a time\n return this._embedBatchSerial(truncated);\n }\n\n /**\n * Check if the service is available.\n */\n isAvailable(): boolean {\n return this.initialized && this.provider !== null;\n }\n\n /**\n * Name of the active provider.\n */\n getProvider(): string | null {\n return this.provider;\n }\n\n /**\n * Embedding vector dimensions.\n */\n getDimensions(): number {\n return 384;\n }\n\n // --- Batch implementations ---\n\n /**\n * Native batch embedding with fastembed.\n * FlagEmbedding.embed() accepts string[] and returns an async iterable of batches.\n */\n private async _embedBatchFastembed(texts: string[]): Promise<(Float32Array | null)[]> {\n const results: (Float32Array | null)[] = [];\n // Pass entire array at once; second param is internal batch size for chunking\n const embeddings = this.model.embed(texts, texts.length);\n for await (const batch of embeddings) {\n if (batch) {\n for (const vec of batch) {\n results.push(vec instanceof Float32Array ? vec : new Float32Array(vec));\n }\n }\n }\n // Pad with nulls if fastembed returned fewer results than expected\n while (results.length < texts.length) {\n results.push(null);\n }\n return results;\n }\n\n /**\n * Batch embedding with @huggingface/transformers pipeline.\n * The pipeline accepts string[] and returns a Tensor with shape [N, dims].\n */\n private async _embedBatchTransformers(texts: string[]): Promise<(Float32Array | null)[]> {\n const output = await this.model(texts, {\n pooling: 'mean',\n normalize: true\n });\n\n if (!output?.data) {\n return texts.map(() => null);\n }\n\n const dims = this.getDimensions();\n const data = output.data instanceof Float32Array\n ? output.data\n : new Float32Array(output.data);\n\n // The output tensor is flattened [N * dims], slice into individual vectors\n const results: (Float32Array | null)[] = [];\n for (let i = 0; i < texts.length; i++) {\n const offset = i * dims;\n if (offset + dims <= data.length) {\n results.push(data.slice(offset, offset + dims));\n } else {\n results.push(null);\n }\n }\n return results;\n }\n\n /**\n * Serial fallback: embed texts one at a time.\n * Used when native batch fails.\n */\n private async _embedBatchSerial(texts: string[]): Promise<(Float32Array | null)[]> {\n const results: (Float32Array | null)[] = [];\n for (const text of texts) {\n try {\n const embedding = await this.embed(text);\n results.push(embedding);\n } catch {\n results.push(null);\n }\n }\n return results;\n }\n\n // --- Single-text provider implementations ---\n\n private async _embedFastembed(text: string): Promise<Float32Array | null> {\n const embeddings = this.model.embed([text], 1);\n for await (const batch of embeddings) {\n if (batch && batch.length > 0) {\n // fastembed returns array of arrays\n const vec = batch[0];\n return vec instanceof Float32Array ? vec : new Float32Array(vec);\n }\n }\n return null;\n }\n\n private async _embedTransformers(text: string): Promise<Float32Array | null> {\n const output = await this.model(text, {\n pooling: 'mean',\n normalize: true\n });\n\n // transformers.js returns a Tensor, extract the data\n if (output?.data) {\n return output.data instanceof Float32Array\n ? output.data\n : new Float32Array(output.data);\n }\n\n return null;\n }\n}\n\n// Singleton\nlet embeddingService: EmbeddingService | null = null;\n\nexport function getEmbeddingService(): EmbeddingService {\n if (!embeddingService) {\n embeddingService = new EmbeddingService();\n }\n return embeddingService;\n}\n", "/**\n * Local vector search on SQLite BLOB\n *\n * Stores embeddings as BLOB in observation_embeddings,\n * computes cosine similarity in JavaScript for semantic search.\n *\n * Optimizations vs brute-force O(n):\n * - SQL pre-filtering by project and recency (reduces candidates)\n * - Maximum limit of candidates loaded in memory (maxCandidates)\n * - Buffer pooling to reduce GC allocations\n */\n\nimport type { Database } from 'bun:sqlite';\nimport { getEmbeddingService } from './EmbeddingService.js';\nimport { logger } from '../../utils/logger.js';\n\n// Maximum number of embeddings loaded in memory per query\nconst DEFAULT_MAX_CANDIDATES = 2000;\n\nexport interface VectorSearchResult {\n id: number;\n observationId: number;\n similarity: number;\n title: string;\n text: string | null;\n type: string;\n project: string;\n created_at: string;\n created_at_epoch: number;\n}\n\n/**\n * Compute cosine similarity between two Float32Array vectors.\n * Optimized version: unified loop with a single final sqrt.\n */\nexport function cosineSimilarity(a: Float32Array, b: Float32Array): number {\n const len = a.length;\n if (len !== b.length) return 0;\n\n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n\n // Unified loop \u2014 avoids 3 separate passes\n for (let i = 0; i < len; i++) {\n const ai = a[i];\n const bi = b[i];\n dotProduct += ai * bi;\n normA += ai * ai;\n normB += bi * bi;\n }\n\n const denominator = Math.sqrt(normA * normB);\n if (denominator === 0) return 0;\n\n return dotProduct / denominator;\n}\n\n/**\n * Convert Float32Array to Buffer for SQLite BLOB storage.\n */\nfunction float32ToBuffer(arr: Float32Array): Buffer {\n return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/**\n * Convert SQLite BLOB Buffer to Float32Array.\n */\nfunction bufferToFloat32(buf: Buffer | Uint8Array): Float32Array {\n const arrayBuffer = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);\n return new Float32Array(arrayBuffer);\n}\n\nexport class VectorSearch {\n\n /**\n * Semantic search with SQL pre-filtering for scalability.\n *\n * 2-phase strategy:\n * 1. SQL pre-filters by project + sorts by recency (loads max N candidates)\n * 2. JS computes cosine similarity only on filtered candidates\n *\n * With 50k observations and maxCandidates=2000, loads only ~4% of data.\n */\n async search(\n db: Database,\n queryEmbedding: Float32Array,\n options: {\n project?: string;\n limit?: number;\n threshold?: number;\n maxCandidates?: number;\n } = {}\n ): Promise<VectorSearchResult[]> {\n const limit = options.limit || 10;\n const threshold = options.threshold || 0.3;\n const maxCandidates = options.maxCandidates || DEFAULT_MAX_CANDIDATES;\n\n try {\n // Phase 1: pre-filter in SQL by project, sort by recency, limit candidates\n const conditions: string[] = [];\n const params: any[] = [];\n\n if (options.project) {\n conditions.push('o.project = ?');\n params.push(options.project);\n }\n\n const whereClause = conditions.length > 0\n ? `WHERE ${conditions.join(' AND ')}`\n : '';\n\n // Sort by recency and limit candidates \u2014 avoids loading all embeddings\n const sql = `\n SELECT e.observation_id, e.embedding,\n o.title, o.text, o.type, o.project, o.created_at, o.created_at_epoch\n FROM observation_embeddings e\n JOIN observations o ON o.id = e.observation_id\n ${whereClause}\n ORDER BY o.created_at_epoch DESC\n LIMIT ?\n `;\n params.push(maxCandidates);\n\n const rows = db.query(sql).all(...params) as Array<{\n observation_id: number;\n embedding: Buffer;\n title: string;\n text: string | null;\n type: string;\n project: string;\n created_at: string;\n created_at_epoch: number;\n }>;\n\n // Phase 2: compute similarity only on pre-filtered candidates\n const scored: VectorSearchResult[] = [];\n\n for (const row of rows) {\n const embedding = bufferToFloat32(row.embedding);\n const similarity = cosineSimilarity(queryEmbedding, embedding);\n\n if (similarity >= threshold) {\n scored.push({\n id: row.observation_id,\n observationId: row.observation_id,\n similarity,\n title: row.title,\n text: row.text,\n type: row.type,\n project: row.project,\n created_at: row.created_at,\n created_at_epoch: row.created_at_epoch\n });\n }\n }\n\n // Sort by similarity descending\n scored.sort((a, b) => b.similarity - a.similarity);\n\n logger.debug('VECTOR', `Search: ${rows.length} candidates \u2192 ${scored.length} above threshold \u2192 ${Math.min(scored.length, limit)} results`);\n\n return scored.slice(0, limit);\n } catch (error) {\n logger.error('VECTOR', `Vector search error: ${error}`);\n return [];\n }\n }\n\n /**\n * Store embedding for an observation.\n */\n async storeEmbedding(\n db: Database,\n observationId: number,\n embedding: Float32Array,\n model: string\n ): Promise<void> {\n try {\n const blob = float32ToBuffer(embedding);\n\n db.query(`\n INSERT OR REPLACE INTO observation_embeddings\n (observation_id, embedding, model, dimensions, created_at)\n VALUES (?, ?, ?, ?, ?)\n `).run(\n observationId,\n blob,\n model,\n embedding.length,\n new Date().toISOString()\n );\n\n logger.debug('VECTOR', `Embedding saved for observation ${observationId}`);\n } catch (error) {\n logger.error('VECTOR', `Error saving embedding: ${error}`);\n }\n }\n\n /**\n * Generate embeddings for observations that don't have them yet.\n */\n async backfillEmbeddings(\n db: Database,\n batchSize: number = 50\n ): Promise<number> {\n const embeddingService = getEmbeddingService();\n if (!await embeddingService.initialize()) {\n logger.warn('VECTOR', 'Embedding service not available, backfill skipped');\n return 0;\n }\n\n // Find observations without embeddings\n const rows = db.query(`\n SELECT o.id, o.title, o.text, o.narrative, o.concepts\n FROM observations o\n LEFT JOIN observation_embeddings e ON e.observation_id = o.id\n WHERE e.observation_id IS NULL\n ORDER BY o.created_at_epoch DESC\n LIMIT ?\n `).all(batchSize) as Array<{\n id: number;\n title: string;\n text: string | null;\n narrative: string | null;\n concepts: string | null;\n }>;\n\n if (rows.length === 0) return 0;\n\n let count = 0;\n const model = embeddingService.getProvider() || 'unknown';\n\n for (const row of rows) {\n // Compose text for embedding: title + text + concepts\n const parts = [row.title];\n if (row.text) parts.push(row.text);\n if (row.narrative) parts.push(row.narrative);\n if (row.concepts) parts.push(row.concepts);\n const fullText = parts.join(' ').substring(0, 2000);\n\n const embedding = await embeddingService.embed(fullText);\n if (embedding) {\n await this.storeEmbedding(db, row.id, embedding, model);\n count++;\n }\n }\n\n logger.info('VECTOR', `Backfill completed: ${count}/${rows.length} embeddings generated`);\n return count;\n }\n\n /**\n * Embedding statistics.\n */\n getStats(db: Database): { total: number; embedded: number; percentage: number } {\n try {\n const totalRow = db.query('SELECT COUNT(*) as count FROM observations').get() as { count: number };\n const embeddedRow = db.query('SELECT COUNT(*) as count FROM observation_embeddings').get() as { count: number };\n\n const total = totalRow?.count || 0;\n const embedded = embeddedRow?.count || 0;\n const percentage = total > 0 ? Math.round((embedded / total) * 100) : 0;\n\n return { total, embedded, percentage };\n } catch {\n return { total: 0, embedded: 0, percentage: 0 };\n }\n }\n}\n\n// Singleton\nlet vectorSearch: VectorSearch | null = null;\n\nexport function getVectorSearch(): VectorSearch {\n if (!vectorSearch) {\n vectorSearch = new VectorSearch();\n }\n return vectorSearch;\n}\n", "/**\n * Scoring engine for intelligent ranking\n *\n * Pure functions with no DB dependencies. Combines 4 signals:\n * - semantic: cosine similarity from embedding\n * - fts5: normalized FTS5 rank\n * - recency: exponential decay based on age\n * - projectMatch: 1 if project matches, 0 otherwise\n */\n\nimport type { ScoringWeights } from '../../types/worker-types.js';\n\n/** Weights for search mode (with text query) */\nexport const SEARCH_WEIGHTS: ScoringWeights = {\n semantic: 0.4,\n fts5: 0.3,\n recency: 0.2,\n projectMatch: 0.1\n};\n\n/** Weights for context mode (no query, e.g. agentSpawn) */\nexport const CONTEXT_WEIGHTS: ScoringWeights = {\n semantic: 0.0,\n fts5: 0.0,\n recency: 0.7,\n projectMatch: 0.3\n};\n\n/**\n * Calculate recency score with exponential decay.\n * More recent = higher (close to 1). After halfLifeHours the score is ~0.5.\n *\n * @param createdAtEpoch - Creation timestamp in milliseconds\n * @param halfLifeHours - Half-life in hours (default: 168 = 7 days)\n * @returns Score 0-1\n */\nexport function recencyScore(createdAtEpoch: number, halfLifeHours: number = 168): number {\n if (!createdAtEpoch || createdAtEpoch <= 0) return 0;\n\n const nowMs = Date.now();\n const ageMs = nowMs - createdAtEpoch;\n\n // If timestamp is in the future, maximum score\n if (ageMs <= 0) return 1;\n\n const ageHours = ageMs / (1000 * 60 * 60);\n\n // Exponential decay: exp(-age * ln(2) / halfLife)\n return Math.exp(-ageHours * Math.LN2 / halfLifeHours);\n}\n\n/**\n * Normalize a raw FTS5 rank into 0-1 range.\n * FTS5 rank is negative: more negative = more relevant.\n * Min-max normalization relative to all ranks in the batch.\n *\n * @param rank - Raw FTS5 rank (negative)\n * @param allRanks - All ranks in the batch for normalization\n * @returns Score 0-1 (1 = most relevant)\n */\nexport function normalizeFTS5Rank(rank: number, allRanks: number[]): number {\n if (allRanks.length === 0) return 0;\n if (allRanks.length === 1) return 1; // Single result: maximum relevance\n\n const minRank = Math.min(...allRanks); // Most negative = best\n const maxRank = Math.max(...allRanks); // Least negative = worst\n\n // If all equal, return 1\n if (minRank === maxRank) return 1;\n\n // Invert: most negative becomes 1, least negative becomes 0\n return (maxRank - rank) / (maxRank - minRank);\n}\n\n/**\n * Binary score for project match.\n *\n * @param itemProject - Project of the item\n * @param targetProject - Target project (e.g. current project)\n * @returns 1 if they match, 0 otherwise\n */\nexport function projectMatchScore(itemProject: string, targetProject: string): number {\n if (!itemProject || !targetProject) return 0;\n return itemProject.toLowerCase() === targetProject.toLowerCase() ? 1 : 0;\n}\n\n/**\n * Calculate weighted composite score combining the 4 signals.\n *\n * @param signals - Values of the 4 signals (each 0-1)\n * @param weights - Weights for each signal\n * @returns Composite score 0-1\n */\nexport function computeCompositeScore(\n signals: {\n semantic: number;\n fts5: number;\n recency: number;\n projectMatch: number;\n },\n weights: ScoringWeights\n): number {\n return (\n signals.semantic * weights.semantic +\n signals.fts5 * weights.fts5 +\n signals.recency * weights.recency +\n signals.projectMatch * weights.projectMatch\n );\n}\n\n/**\n * Recency score based on last access (search that found the observation).\n * Uses shorter half-life than recencyScore() because access is more volatile.\n *\n * If the observation was never accessed, returns 0 (maximum penalty).\n *\n * @param lastAccessedEpoch - Last access timestamp in milliseconds (null if never accessed)\n * @param halfLifeHours - Half-life in hours (default: 48 = 2 days)\n * @returns Score 0-1 (1 = recently accessed)\n */\nexport function accessRecencyScore(lastAccessedEpoch: number | null, halfLifeHours: number = 48): number {\n if (!lastAccessedEpoch || lastAccessedEpoch <= 0) return 0;\n\n const nowMs = Date.now();\n const ageMs = nowMs - lastAccessedEpoch;\n\n // If timestamp is in the future, maximum score\n if (ageMs <= 0) return 1;\n\n const ageHours = ageMs / (1000 * 60 * 60);\n return Math.exp(-ageHours * Math.LN2 / halfLifeHours);\n}\n\n/**\n * Penalty for stale observations (files modified after the observation).\n * Returns a multiplier: 1.0 if fresh, 0.5 if stale.\n * Does not remove the observation from ranking but penalizes it significantly.\n *\n * @param isStale - Stale flag (0 = fresh, 1 = stale)\n * @returns Multiplier 0.5-1.0\n */\nexport function stalenessPenalty(isStale: number): number {\n return isStale === 1 ? 0.5 : 1.0;\n}\n\n/**\n * Multiplicative boost for structured knowledge types.\n * Constraint and decision weigh more because they represent critical rules and choices.\n * Non-knowledge types stay at 1.0 (no boost).\n */\nexport const KNOWLEDGE_TYPE_BOOST: Record<string, number> = {\n constraint: 1.30,\n decision: 1.25,\n heuristic: 1.15,\n rejected: 1.10\n};\n\n/**\n * Returns the boost multiplier for the observation type.\n * Knowledge types get a boost, all others stay at 1.0.\n *\n * @param type - Observation type\n * @returns Multiplier >= 1.0\n */\nexport function knowledgeTypeBoost(type: string): number {\n return KNOWLEDGE_TYPE_BOOST[type] ?? 1.0;\n}\n\n/**\n * Approximate token estimation from a string.\n * Uses the rule of thumb: 1 token \u2248 4 characters.\n */\nexport function estimateTokens(text: string): number {\n if (!text) return 0;\n return Math.ceil(text.length / 4);\n}\n", "/**\n * Hybrid search: combines local vector search (SQLite BLOB) with keyword search (FTS5)\n *\n * 4-signal scoring:\n * - semantic: cosine similarity from embedding\n * - fts5: normalized FTS5 rank\n * - recency: exponential decay\n * - projectMatch: project match\n *\n * If the embedding service is not available, falls back to FTS5 only.\n */\n\nimport { getEmbeddingService } from './EmbeddingService.js';\nimport { getVectorSearch } from './VectorSearch.js';\nimport {\n recencyScore,\n normalizeFTS5Rank,\n projectMatchScore,\n computeCompositeScore,\n knowledgeTypeBoost,\n SEARCH_WEIGHTS\n} from './ScoringEngine.js';\nimport type { Database } from 'bun:sqlite';\nimport type { ScoringWeights } from '../../types/worker-types.js';\nimport { logger } from '../../utils/logger.js';\n\nexport interface SearchResult {\n id: string;\n title: string;\n content: string;\n type: string;\n project: string;\n created_at: string;\n created_at_epoch: number;\n score: number;\n source: 'vector' | 'keyword' | 'hybrid';\n signals: {\n semantic: number;\n fts5: number;\n recency: number;\n projectMatch: number;\n };\n}\n\nexport class HybridSearch {\n private embeddingInitialized = false;\n\n /**\n * Initialize the embedding service (lazy, non-blocking)\n */\n async initialize(): Promise<void> {\n try {\n const embeddingService = getEmbeddingService();\n await embeddingService.initialize();\n this.embeddingInitialized = embeddingService.isAvailable();\n logger.info('SEARCH', `HybridSearch initialized (embedding: ${this.embeddingInitialized ? 'active' : 'disabled'})`);\n } catch (error) {\n logger.warn('SEARCH', 'Embedding initialization failed, using only FTS5', {}, error as Error);\n this.embeddingInitialized = false;\n }\n }\n\n /**\n * Hybrid search with 4-signal scoring\n */\n async search(\n db: Database,\n query: string,\n options: {\n project?: string;\n limit?: number;\n weights?: ScoringWeights;\n } = {}\n ): Promise<SearchResult[]> {\n const limit = options.limit || 10;\n const weights = options.weights || SEARCH_WEIGHTS;\n const targetProject = options.project || '';\n\n // Collect raw results from both sources\n const rawItems = new Map<string, {\n id: string;\n title: string;\n content: string;\n type: string;\n project: string;\n created_at: string;\n created_at_epoch: number;\n semanticScore: number;\n fts5Rank: number | null; // raw rank, to be normalized later\n source: 'vector' | 'keyword';\n }>();\n\n // Vector search (if embedding available)\n if (this.embeddingInitialized) {\n try {\n const embeddingService = getEmbeddingService();\n const queryEmbedding = await embeddingService.embed(query);\n\n if (queryEmbedding) {\n const vectorSearch = getVectorSearch();\n const vectorResults = await vectorSearch.search(db, queryEmbedding, {\n project: options.project,\n limit: limit * 2, // Fetch more results for ranking\n threshold: 0.3\n });\n\n for (const hit of vectorResults) {\n rawItems.set(String(hit.observationId), {\n id: String(hit.observationId),\n title: hit.title,\n content: hit.text || '',\n type: hit.type,\n project: hit.project,\n created_at: hit.created_at,\n created_at_epoch: hit.created_at_epoch,\n semanticScore: hit.similarity,\n fts5Rank: null,\n source: 'vector'\n });\n }\n\n logger.debug('SEARCH', `Vector search: ${vectorResults.length} results`);\n }\n } catch (error) {\n logger.warn('SEARCH', 'Vector search failed, using only keyword', {}, error as Error);\n }\n }\n\n // Keyword search FTS5 with rank (always active)\n try {\n const { searchObservationsFTSWithRank } = await import('../sqlite/Search.js');\n const keywordResults = searchObservationsFTSWithRank(db, query, {\n project: options.project,\n limit: limit * 2\n });\n\n for (const obs of keywordResults) {\n const id = String(obs.id);\n const existing = rawItems.get(id);\n\n if (existing) {\n // Present in both sources: add FTS5 rank\n existing.fts5Rank = obs.fts5_rank;\n existing.source = 'vector'; // Keep vector as primary source\n } else {\n rawItems.set(id, {\n id,\n title: obs.title,\n content: obs.text || obs.narrative || '',\n type: obs.type,\n project: obs.project,\n created_at: obs.created_at,\n created_at_epoch: obs.created_at_epoch,\n semanticScore: 0,\n fts5Rank: obs.fts5_rank,\n source: 'keyword'\n });\n }\n }\n\n logger.debug('SEARCH', `Keyword search: ${keywordResults.length} results`);\n } catch (error) {\n logger.error('SEARCH', 'Keyword search failed', {}, error as Error);\n }\n\n // No results\n if (rawItems.size === 0) return [];\n\n // Normalize FTS5 ranks\n const allFTS5Ranks = Array.from(rawItems.values())\n .filter(item => item.fts5Rank !== null)\n .map(item => item.fts5Rank as number);\n\n // Compute composite score for each item\n const scored: SearchResult[] = [];\n\n for (const item of rawItems.values()) {\n const signals = {\n semantic: item.semanticScore,\n fts5: item.fts5Rank !== null ? normalizeFTS5Rank(item.fts5Rank, allFTS5Ranks) : 0,\n recency: recencyScore(item.created_at_epoch),\n projectMatch: targetProject ? projectMatchScore(item.project, targetProject) : 0\n };\n\n const score = computeCompositeScore(signals, weights);\n\n // Boost for items present in both sources\n const isHybrid = item.semanticScore > 0 && item.fts5Rank !== null;\n const hybridBoost = isHybrid ? 1.15 : 1.0;\n // Boost for knowledge types (constraint, decision, heuristic, rejected)\n const finalScore = Math.min(1, score * hybridBoost * knowledgeTypeBoost(item.type));\n\n scored.push({\n id: item.id,\n title: item.title,\n content: item.content,\n type: item.type,\n project: item.project,\n created_at: item.created_at,\n created_at_epoch: item.created_at_epoch,\n score: finalScore,\n source: isHybrid ? 'hybrid' : item.source,\n signals\n });\n }\n\n // Sort by score descending and limit\n scored.sort((a, b) => b.score - a.score);\n const finalResults = scored.slice(0, limit);\n\n // Access tracking: update last_accessed_epoch for found results (fire-and-forget)\n if (finalResults.length > 0) {\n try {\n const { updateLastAccessed } = await import('../sqlite/Observations.js');\n const ids = finalResults.map(r => parseInt(r.id, 10)).filter(id => id > 0);\n if (ids.length > 0) {\n updateLastAccessed(db, ids);\n }\n } catch {\n // Don't propagate errors \u2014 access tracking is optional\n }\n }\n\n return finalResults;\n }\n}\n\n// Singleton\nlet hybridSearch: HybridSearch | null = null;\n\nexport function getHybridSearch(): HybridSearch {\n if (!hybridSearch) {\n hybridSearch = new HybridSearch();\n }\n return hybridSearch;\n}\n", "/**\n * Shared context for all worker routers.\n * Centralizes database, SSE broadcast, cache, and validation helpers.\n */\n\nimport type { Response } from 'express';\nimport { KiroMemoryDatabase } from './sqlite/Database.js';\nimport { getEmbeddingService } from './search/EmbeddingService.js';\nimport { getVectorSearch } from './search/VectorSearch.js';\nimport { logger } from '../utils/logger.js';\n\n// \u2500\u2500 Shared context type \u2500\u2500\n\nexport interface WorkerContext {\n db: KiroMemoryDatabase;\n broadcast: (event: string, data: any) => void;\n invalidateProjectsCache: () => void;\n generateEmbeddingForObservation: (\n observationId: number,\n title: string,\n content: string | null,\n concepts?: string[]\n ) => Promise<void>;\n}\n\n// \u2500\u2500 SSE Client Management \u2500\u2500\n\nconst MAX_SSE_CLIENTS = 50;\nconst clients: Response[] = [];\n\nexport function getClients(): Response[] {\n return clients;\n}\n\nexport function getMaxSSEClients(): number {\n return MAX_SSE_CLIENTS;\n}\n\nexport function addClient(res: Response): void {\n clients.push(res);\n}\n\nexport function removeClient(res: Response): void {\n const index = clients.indexOf(res);\n if (index > -1) {\n clients.splice(index, 1);\n }\n}\n\n/** Broadcast SSE event to all connected clients */\nexport function broadcast(event: string, data: any): void {\n const message = `event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`;\n clients.forEach(client => {\n try {\n client.write(message);\n } catch (err) {\n logger.warn('WORKER', 'Broadcast failed to client', {}, err as Error);\n }\n });\n}\n\n// \u2500\u2500 Projects cache \u2500\u2500\n\nexport let projectsCache: { data: string[]; ts: number } = { data: [], ts: 0 };\nexport const PROJECTS_CACHE_TTL = 60_000;\n\nexport function invalidateProjectsCache(): void {\n projectsCache.ts = 0;\n}\n\n// \u2500\u2500 Validation helpers (shared across all routers) \u2500\u2500\n\n/** Parse an integer with safe range, returns default if invalid */\nexport function parseIntSafe(value: string | undefined, defaultVal: number, min: number, max: number): number {\n if (!value) return defaultVal;\n const parsed = parseInt(value, 10);\n if (isNaN(parsed) || parsed < min || parsed > max) return defaultVal;\n return parsed;\n}\n\n/** Validate that a project name contains only safe characters */\nexport function isValidProject(project: unknown): project is string {\n return typeof project === 'string'\n && project.length > 0\n && project.length <= 200\n && /^[\\w\\-\\.\\/@ ]+$/.test(project)\n && !project.includes('..');\n}\n\n/** Validate a non-empty string with maximum length */\nexport function isValidString(val: unknown, maxLen: number): val is string {\n return typeof val === 'string' && val.length > 0 && val.length <= maxLen;\n}\n\n// \u2500\u2500 Embedding helper \u2500\u2500\n\n/** Generate embedding for an observation (fire-and-forget) */\nexport async function generateEmbeddingForObservation(\n db: KiroMemoryDatabase,\n observationId: number,\n title: string,\n content: string | null,\n concepts?: string[]\n): Promise<void> {\n try {\n const embeddingService = getEmbeddingService();\n if (!embeddingService.isAvailable()) return;\n\n const parts = [title];\n if (content) parts.push(content);\n if (concepts?.length) parts.push(concepts.join(', '));\n const fullText = parts.join(' ').substring(0, 2000);\n\n const embedding = await embeddingService.embed(fullText);\n if (embedding) {\n const vectorSearch = getVectorSearch();\n await vectorSearch.storeEmbedding(\n db.db,\n observationId,\n embedding,\n embeddingService.getProvider() || 'unknown'\n );\n }\n } catch (error) {\n logger.debug('WORKER', `Embedding generation failed for obs ${observationId}: ${error}`);\n }\n}\n\n// \u2500\u2500 Context factory \u2500\u2500\n\nexport function createWorkerContext(db: KiroMemoryDatabase): WorkerContext {\n return {\n db,\n broadcast,\n invalidateProjectsCache,\n generateEmbeddingForObservation: (id, title, content, concepts) =>\n generateEmbeddingForObservation(db, id, title, content, concepts),\n };\n}\n", "/**\n * Router Core: health check, SSE events, notify endpoint.\n * Manages the base worker infrastructure.\n */\n\nimport { Router } from 'express';\nimport rateLimit from 'express-rate-limit';\nimport type { WorkerContext } from '../worker-context.js';\nimport { getClients, getMaxSSEClients, addClient, removeClient } from '../worker-context.js';\nimport { logger } from '../../utils/logger.js';\n\nconst ALLOWED_EVENTS = new Set([\n 'observation-created',\n 'summary-created',\n 'prompt-created',\n 'session-created'\n]);\n\nexport function createCoreRouter(ctx: WorkerContext, workerToken: string): Router {\n const router = Router();\n\n // Dedicated rate limit for /api/notify (more restrictive)\n const notifyLimiter = rateLimit({\n windowMs: 60_000,\n max: 60,\n standardHeaders: true,\n legacyHeaders: false\n });\n\n // Notification from hooks \u2192 SSE broadcast to dashboard clients\n router.post('/api/notify', notifyLimiter, (req, res) => {\n const token = req.headers['x-worker-token'] as string;\n if (token !== workerToken) {\n res.status(401).json({ error: 'Invalid or missing X-Worker-Token' });\n return;\n }\n\n const { event, data } = req.body || {};\n if (!event || typeof event !== 'string' || !ALLOWED_EVENTS.has(event)) {\n res.status(400).json({ error: `Event must be one of: ${[...ALLOWED_EVENTS].join(', ')}` });\n return;\n }\n\n ctx.broadcast(event, data || {});\n res.json({ ok: true });\n });\n\n // Health check\n router.get('/health', (_req, res) => {\n res.json({\n status: 'ok',\n timestamp: Date.now(),\n version: '1.9.0'\n });\n });\n\n // SSE endpoint with keepalive and connection limit\n router.get('/events', (req, res) => {\n const clients = getClients();\n if (clients.length >= getMaxSSEClients()) {\n res.status(503).json({ error: 'Too many SSE connections' });\n return;\n }\n\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no');\n res.flushHeaders();\n\n addClient(res);\n logger.info('WORKER', 'SSE client connected', { clients: clients.length });\n\n // Initial connection event\n res.write(`event: connected\\ndata: ${JSON.stringify({ timestamp: Date.now() })}\\n\\n`);\n\n // Keepalive every 15 seconds\n const keepaliveInterval = setInterval(() => {\n try {\n res.write(`:keepalive ${Date.now()}\\n\\n`);\n } catch {\n clearInterval(keepaliveInterval);\n }\n }, 15000);\n\n req.on('close', () => {\n clearInterval(keepaliveInterval);\n removeClient(res);\n logger.info('WORKER', 'SSE client disconnected', { clients: getClients().length });\n });\n });\n\n return router;\n}\n", "/**\n * Router Observations: CRUD observations, batch, knowledge, memory save.\n */\n\nimport { Router } from 'express';\nimport type { WorkerContext } from '../worker-context.js';\nimport { isValidProject, isValidString, parseIntSafe } from '../worker-context.js';\nimport { getObservationsByProject, createObservation } from '../sqlite/Observations.js';\nimport { getSummariesByProject } from '../sqlite/Summaries.js';\nimport { getObservationsByIds } from '../sqlite/Search.js';\nimport { KNOWLEDGE_TYPES } from '../../types/worker-types.js';\nimport type { KnowledgeMetadata } from '../../types/worker-types.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createObservationsRouter(ctx: WorkerContext): Router {\n const router = Router();\n\n // Paginated observations list\n router.get('/api/observations', (req, res) => {\n const { offset, limit, project } = req.query as { offset?: string; limit?: string; project?: string };\n const _offset = parseIntSafe(offset, 0, 0, 1_000_000);\n const _limit = parseIntSafe(limit, 50, 1, 200);\n\n try {\n const countSql = project\n ? 'SELECT COUNT(*) as total FROM observations WHERE project = ?'\n : 'SELECT COUNT(*) as total FROM observations';\n const countStmt = ctx.db.db.query(countSql);\n const { total } = (project ? countStmt.get(project) : countStmt.get()) as { total: number };\n\n const sql = project\n ? 'SELECT * FROM observations WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ? OFFSET ?'\n : 'SELECT * FROM observations ORDER BY created_at_epoch DESC LIMIT ? OFFSET ?';\n const stmt = ctx.db.db.query(sql);\n const rows = project ? stmt.all(project, _limit, _offset) : stmt.all(_limit, _offset);\n res.setHeader('X-Total-Count', total);\n res.json(rows);\n } catch (error) {\n logger.error('WORKER', 'Observation list failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to list observations' });\n }\n });\n\n // Create observation\n router.post('/api/observations', (req, res) => {\n const { memorySessionId, project, type, title, content, concepts, files } = req.body;\n\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid or missing \"project\"' });\n return;\n }\n if (!isValidString(title, 500)) {\n res.status(400).json({ error: 'Invalid or missing \"title\" (max 500 chars)' });\n return;\n }\n if (content && !isValidString(content, 100_000)) {\n res.status(400).json({ error: '\"content\" too large (max 100KB)' });\n return;\n }\n if (concepts && !Array.isArray(concepts)) {\n res.status(400).json({ error: '\"concepts\" must be an array' });\n return;\n }\n if (files && !Array.isArray(files)) {\n res.status(400).json({ error: '\"files\" must be an array' });\n return;\n }\n\n try {\n const id = createObservation(\n ctx.db.db,\n memorySessionId || 'api-' + Date.now(),\n project,\n type || 'manual',\n title,\n null,\n content,\n null,\n null,\n concepts?.join(', ') || null,\n files?.join(', ') || null,\n null,\n 0\n );\n\n ctx.broadcast('observation-created', { id, project, title });\n ctx.invalidateProjectsCache();\n\n // Background embedding\n ctx.generateEmbeddingForObservation(id, title, content, concepts).catch(() => {});\n\n res.json({ id, success: true });\n } catch (error) {\n logger.error('WORKER', 'Observation creation failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to store observation' });\n }\n });\n\n // Batch fetch observations by ID (max 100)\n router.post('/api/observations/batch', (req, res) => {\n const { ids } = req.body;\n\n if (!ids || !Array.isArray(ids) || ids.length === 0 || ids.length > 100) {\n res.status(400).json({ error: '\"ids\" must be an array of 1-100 elements' });\n return;\n }\n if (!ids.every((id: unknown) => typeof id === 'number' && Number.isInteger(id) && id > 0)) {\n res.status(400).json({ error: 'All IDs must be positive integers' });\n return;\n }\n\n try {\n const observations = getObservationsByIds(ctx.db.db, ids);\n res.json({ observations });\n } catch (error) {\n logger.error('WORKER', 'Batch fetch failed', { ids }, error as Error);\n res.status(500).json({ error: 'Batch fetch failed' });\n }\n });\n\n // Store structured knowledge\n router.post('/api/knowledge', (req, res) => {\n const { project, knowledge_type, title, content, concepts, files,\n severity, alternatives, reason, context: metaContext, confidence } = req.body;\n\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid or missing \"project\"' });\n return;\n }\n if (!knowledge_type || !KNOWLEDGE_TYPES.includes(knowledge_type)) {\n res.status(400).json({ error: `Invalid \"knowledge_type\". Must be one of: ${KNOWLEDGE_TYPES.join(', ')}` });\n return;\n }\n if (!isValidString(title, 500)) {\n res.status(400).json({ error: 'Invalid or missing \"title\" (max 500 chars)' });\n return;\n }\n if (!isValidString(content, 100_000)) {\n res.status(400).json({ error: 'Invalid or missing \"content\" (max 100KB)' });\n return;\n }\n if (concepts && !Array.isArray(concepts)) {\n res.status(400).json({ error: '\"concepts\" must be an array' });\n return;\n }\n if (files && !Array.isArray(files)) {\n res.status(400).json({ error: '\"files\" must be an array' });\n return;\n }\n\n try {\n let metadata: KnowledgeMetadata;\n switch (knowledge_type) {\n case 'constraint':\n metadata = { knowledgeType: 'constraint', severity: severity === 'hard' ? 'hard' : 'soft', reason };\n break;\n case 'decision':\n metadata = { knowledgeType: 'decision', alternatives, reason };\n break;\n case 'heuristic':\n metadata = { knowledgeType: 'heuristic', context: metaContext, confidence: ['high', 'medium', 'low'].includes(confidence) ? confidence : undefined };\n break;\n case 'rejected':\n metadata = { knowledgeType: 'rejected', reason: reason || '', alternatives };\n break;\n default:\n res.status(400).json({ error: 'Invalid knowledge_type' });\n return;\n }\n\n const id = createObservation(\n ctx.db.db,\n 'api-' + Date.now(),\n project,\n knowledge_type,\n title,\n null,\n content,\n null,\n JSON.stringify(metadata),\n concepts?.join(', ') || null,\n files?.join(', ') || null,\n null,\n 0\n );\n\n ctx.broadcast('observation-created', { id, project, title, type: knowledge_type });\n ctx.generateEmbeddingForObservation(id, title, content, concepts).catch(() => {});\n\n res.json({ id, success: true, knowledge_type });\n } catch (error) {\n logger.error('WORKER', 'Knowledge save failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to store knowledge' });\n }\n });\n\n // Save memory (programmable endpoint)\n router.post('/api/memory/save', (req, res) => {\n const { project, title, content, type, concepts } = req.body;\n\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid or missing \"project\"' });\n return;\n }\n if (!isValidString(title, 500)) {\n res.status(400).json({ error: 'Invalid or missing \"title\" (max 500 chars)' });\n return;\n }\n if (!isValidString(content, 100_000)) {\n res.status(400).json({ error: 'Invalid or missing \"content\" (max 100KB)' });\n return;\n }\n\n const obsType = type || 'research';\n const conceptStr = Array.isArray(concepts) ? concepts.join(', ') : (concepts || null);\n\n try {\n const id = createObservation(\n ctx.db.db,\n 'memory-save-' + Date.now(),\n project,\n obsType,\n title,\n null,\n content,\n content,\n null,\n conceptStr,\n null,\n null,\n 0\n );\n\n ctx.broadcast('observation-created', { id, project, title });\n ctx.invalidateProjectsCache();\n ctx.generateEmbeddingForObservation(id, title, content, Array.isArray(concepts) ? concepts : undefined).catch(() => {});\n\n res.json({ id, success: true });\n } catch (error) {\n logger.error('WORKER', 'Memory save failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to save memory' });\n }\n });\n\n // Context by project\n router.get('/api/context/:project', (req, res) => {\n const { project } = req.params;\n\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const context = {\n project,\n observations: getObservationsByProject(ctx.db.db, project, 20),\n summaries: getSummariesByProject(ctx.db.db, project, 5)\n };\n res.json(context);\n } catch (error) {\n logger.error('WORKER', 'Context retrieval failed', { project }, error as Error);\n res.status(500).json({ error: 'Failed to get context' });\n }\n });\n\n return router;\n}\n", "import { Database } from 'bun:sqlite';\nimport type { Summary } from '../../types/worker-types.js';\n\n/**\n * Summary operations for Kiro Memory database\n */\n\n/** Escape dei caratteri wildcard LIKE per prevenire pattern injection */\nfunction escapeLikePattern(input: string): string {\n return input.replace(/[%_\\\\]/g, '\\\\$&');\n}\n\nexport function createSummary(\n db: Database,\n sessionId: string,\n project: string,\n request: string | null,\n investigated: string | null,\n learned: string | null,\n completed: string | null,\n nextSteps: string | null,\n notes: string | null\n): number {\n const now = new Date();\n const result = db.run(\n `INSERT INTO summaries \n (session_id, project, request, investigated, learned, completed, next_steps, notes, created_at, created_at_epoch)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [sessionId, project, request, investigated, learned, completed, nextSteps, notes, now.toISOString(), now.getTime()]\n );\n return Number(result.lastInsertRowid);\n}\n\nexport function getSummaryBySession(db: Database, sessionId: string): Summary | null {\n const query = db.query('SELECT * FROM summaries WHERE session_id = ? ORDER BY created_at_epoch DESC LIMIT 1');\n return query.get(sessionId) as Summary | null;\n}\n\nexport function getSummariesByProject(db: Database, project: string, limit: number = 50): Summary[] {\n const query = db.query(\n 'SELECT * FROM summaries WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ?'\n );\n return query.all(project, limit) as Summary[];\n}\n\nexport function searchSummaries(db: Database, searchTerm: string, project?: string): Summary[] {\n const sql = project\n ? `SELECT * FROM summaries\n WHERE project = ? AND (request LIKE ? ESCAPE '\\\\' OR learned LIKE ? ESCAPE '\\\\' OR completed LIKE ? ESCAPE '\\\\' OR notes LIKE ? ESCAPE '\\\\')\n ORDER BY created_at_epoch DESC`\n : `SELECT * FROM summaries\n WHERE request LIKE ? ESCAPE '\\\\' OR learned LIKE ? ESCAPE '\\\\' OR completed LIKE ? ESCAPE '\\\\' OR notes LIKE ? ESCAPE '\\\\'\n ORDER BY created_at_epoch DESC`;\n\n const pattern = `%${escapeLikePattern(searchTerm)}%`;\n const query = db.query(sql);\n\n if (project) {\n return query.all(project, pattern, pattern, pattern, pattern) as Summary[];\n }\n return query.all(pattern, pattern, pattern, pattern) as Summary[];\n}\n\nexport function deleteSummary(db: Database, id: number): void {\n db.run('DELETE FROM summaries WHERE id = ?', [id]);\n}\n", "/**\n * Shared types for Kiro Memory Worker Service\n */\n\n// ============================================================================\n// Pagination Types\n// ============================================================================\n\nexport interface PaginatedResult<T> {\n items: T[];\n hasMore: boolean;\n offset: number;\n limit: number;\n}\n\nexport interface PaginationParams {\n offset: number;\n limit: number;\n project?: string;\n}\n\n// ============================================================================\n// Database Record Types\n// ============================================================================\n\nexport interface Observation {\n id: number;\n memory_session_id: string;\n project: string;\n type: string;\n title: string;\n subtitle: string | null;\n text: string | null;\n narrative: string | null;\n facts: string | null;\n concepts: string | null;\n files_read: string | null;\n files_modified: string | null;\n prompt_number: number;\n created_at: string;\n created_at_epoch: number;\n last_accessed_epoch: number | null;\n is_stale: number; // 0 = fresh, 1 = file modified after the observation\n}\n\nexport interface Summary {\n id: number;\n session_id: string;\n project: string;\n request: string | null;\n investigated: string | null;\n learned: string | null;\n completed: string | null;\n next_steps: string | null;\n notes: string | null;\n created_at: string;\n created_at_epoch: number;\n}\n\nexport interface UserPrompt {\n id: number;\n content_session_id: string;\n project: string;\n prompt_number: number;\n prompt_text: string;\n created_at: string;\n created_at_epoch: number;\n}\n\nexport interface DBSession {\n id: number;\n content_session_id: string;\n project: string;\n user_prompt: string;\n memory_session_id: string | null;\n status: 'active' | 'completed' | 'failed';\n started_at: string;\n started_at_epoch: number;\n completed_at: string | null;\n completed_at_epoch: number | null;\n}\n\nexport interface DBCheckpoint {\n id: number;\n session_id: number;\n project: string;\n task: string;\n progress: string | null;\n next_steps: string | null;\n open_questions: string | null;\n relevant_files: string | null;\n context_snapshot: string | null;\n created_at: string;\n created_at_epoch: number;\n}\n\n// ============================================================================\n// Report Types\n// ============================================================================\n\nexport interface ReportData {\n period: {\n start: string;\n end: string;\n days: number;\n label: string;\n };\n overview: {\n observations: number;\n summaries: number;\n sessions: number;\n prompts: number;\n knowledgeCount: number;\n staleCount: number;\n };\n timeline: Array<{ day: string; count: number }>;\n typeDistribution: Array<{ type: string; count: number }>;\n sessionStats: {\n total: number;\n completed: number;\n avgDurationMinutes: number;\n };\n topLearnings: string[];\n completedTasks: string[];\n nextSteps: string[];\n fileHotspots: Array<{ file: string; count: number }>;\n}\n\n// ============================================================================\n// Kiro Integration Types\n// ============================================================================\n\nexport interface KiroMessage {\n role: 'user' | 'assistant' | 'system';\n content: string;\n timestamp?: number;\n}\n\nexport interface KiroSession {\n id: string;\n project: string;\n messages: KiroMessage[];\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface ContextContext {\n project: string;\n relevantObservations: Observation[];\n relevantSummaries: Summary[];\n recentPrompts: UserPrompt[];\n}\n\n// ============================================================================\n// Kiro CLI Hook Types\n// ============================================================================\n\n/**\n * Input JSON received via stdin from Kiro CLI / Claude Code hooks\n *\n * Fields common to all hooks:\n * - session_id, cwd, hook_event_name\n *\n * Hook-specific fields:\n * - UserPromptSubmit: prompt (user text, top-level)\n * - PostToolUse: tool_name, tool_input, tool_response, tool_use_id\n * - Stop: stop_hook_active, transcript_path\n * - SessionStart/agentSpawn: transcript_path\n */\nexport interface KiroHookInput {\n hook_event_name: string;\n session_id?: string;\n cwd: string;\n transcript_path?: string;\n permission_mode?: string;\n\n // UserPromptSubmit: the prompt is top-level, NOT in tool_input\n prompt?: string;\n user_prompt?: string;\n\n // PostToolUse / PreToolUse\n tool_name?: string;\n tool_input?: any;\n tool_response?: any;\n tool_use_id?: string;\n\n // Stop\n stop_hook_active?: boolean;\n\n // Catch-all for fields not yet mapped\n [key: string]: any;\n}\n\n// ============================================================================\n// Search Types\n// ============================================================================\n\nexport interface SearchFilters {\n project?: string;\n type?: string;\n dateStart?: number;\n dateEnd?: number;\n limit?: number;\n}\n\nexport interface SearchResult {\n observations: Observation[];\n summaries: Summary[];\n total: number;\n}\n\nexport interface TimelineEntry {\n id: number;\n type: 'observation' | 'summary' | 'session';\n title: string;\n content: string | null;\n project: string;\n created_at: string;\n created_at_epoch: number;\n}\n\n// ============================================================================\n// Smart Ranking Types (Phase 2B)\n// ============================================================================\n\n/** Weights for the 4 scoring signals */\nexport interface ScoringWeights {\n semantic: number;\n fts5: number;\n recency: number;\n projectMatch: number;\n}\n\n/** Item with composite score and individual signals */\nexport interface ScoredItem {\n id: number;\n title: string;\n content: string;\n type: string;\n project: string;\n created_at: string;\n created_at_epoch: number;\n score: number;\n signals: {\n semantic: number;\n fts5: number;\n recency: number;\n projectMatch: number;\n };\n}\n\n/** Smart context with token budget */\nexport interface SmartContext {\n project: string;\n items: ScoredItem[];\n summaries: Summary[];\n tokenBudget: number;\n tokensUsed: number;\n}\n\n// ============================================================================\n// Structured Knowledge Types (Phase 5A)\n// ============================================================================\n\n/** Structured knowledge types */\nexport type KnowledgeType = 'constraint' | 'decision' | 'heuristic' | 'rejected';\n\n/** Constant for runtime validation */\nexport const KNOWLEDGE_TYPES: KnowledgeType[] = ['constraint', 'decision', 'heuristic', 'rejected'];\n\n/** Metadata for constraints (hard/soft rules) */\nexport interface ConstraintMeta {\n knowledgeType: 'constraint';\n severity: 'hard' | 'soft';\n reason?: string;\n}\n\n/** Metadata for architectural decisions */\nexport interface DecisionMeta {\n knowledgeType: 'decision';\n alternatives?: string[];\n reason?: string;\n}\n\n/** Metadata for preferences/heuristics */\nexport interface HeuristicMeta {\n knowledgeType: 'heuristic';\n context?: string;\n confidence?: 'high' | 'medium' | 'low';\n}\n\n/** Metadata for rejected solutions */\nexport interface RejectedMeta {\n knowledgeType: 'rejected';\n reason: string;\n alternatives?: string[];\n}\n\n/** Discriminated union for knowledge metadata */\nexport type KnowledgeMetadata = ConstraintMeta | DecisionMeta | HeuristicMeta | RejectedMeta;\n\n/** Input for storing structured knowledge */\nexport interface StoreKnowledgeInput {\n project: string;\n knowledgeType: KnowledgeType;\n title: string;\n content: string;\n concepts?: string[];\n files?: string[];\n /** Type-specific metadata (severity, alternatives, reason, context, confidence) */\n metadata?: Partial<Omit<ConstraintMeta, 'knowledgeType'>> &\n Partial<Omit<DecisionMeta, 'knowledgeType'>> &\n Partial<Omit<HeuristicMeta, 'knowledgeType'>> &\n Partial<Omit<RejectedMeta, 'knowledgeType'>>;\n}\n", "/**\n * Router Summaries: session summary CRUD.\n */\n\nimport { Router } from 'express';\nimport type { WorkerContext } from '../worker-context.js';\nimport { isValidProject, isValidString, parseIntSafe } from '../worker-context.js';\nimport { createSummary } from '../sqlite/Summaries.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createSummariesRouter(ctx: WorkerContext): Router {\n const router = Router();\n\n // Paginated summary list\n router.get('/api/summaries', (req, res) => {\n const { offset, limit, project } = req.query as { offset?: string; limit?: string; project?: string };\n const _offset = parseIntSafe(offset, 0, 0, 1_000_000);\n const _limit = parseIntSafe(limit, 20, 1, 200);\n\n try {\n const countSql = project\n ? 'SELECT COUNT(*) as total FROM summaries WHERE project = ?'\n : 'SELECT COUNT(*) as total FROM summaries';\n const countStmt = ctx.db.db.query(countSql);\n const { total } = (project ? countStmt.get(project) : countStmt.get()) as { total: number };\n\n const sql = project\n ? 'SELECT * FROM summaries WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ? OFFSET ?'\n : 'SELECT * FROM summaries ORDER BY created_at_epoch DESC LIMIT ? OFFSET ?';\n const stmt = ctx.db.db.query(sql);\n const rows = project ? stmt.all(project, _limit, _offset) : stmt.all(_limit, _offset);\n res.setHeader('X-Total-Count', total);\n res.json(rows);\n } catch (error) {\n logger.error('WORKER', 'Summary list failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to list summaries' });\n }\n });\n\n // Create summary\n router.post('/api/summaries', (req, res) => {\n const { sessionId, project, request, learned, completed, nextSteps } = req.body;\n\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid or missing \"project\"' });\n return;\n }\n const MAX_FIELD = 50_000;\n if (request && !isValidString(request, MAX_FIELD)) { res.status(400).json({ error: '\"request\" too large' }); return; }\n if (learned && !isValidString(learned, MAX_FIELD)) { res.status(400).json({ error: '\"learned\" too large' }); return; }\n if (completed && !isValidString(completed, MAX_FIELD)) { res.status(400).json({ error: '\"completed\" too large' }); return; }\n if (nextSteps && !isValidString(nextSteps, MAX_FIELD)) { res.status(400).json({ error: '\"nextSteps\" too large' }); return; }\n\n try {\n const id = createSummary(\n ctx.db.db,\n sessionId || 'api-' + Date.now(),\n project,\n request || null,\n null,\n learned || null,\n completed || null,\n nextSteps || null,\n null\n );\n\n ctx.broadcast('summary-created', { id, project });\n res.json({ id, success: true });\n } catch (error) {\n logger.error('WORKER', 'Summary creation failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to store summary' });\n }\n });\n\n return router;\n}\n", "/**\n * Router Search: FTS5 search, hybrid search, timeline.\n */\n\nimport { Router } from 'express';\nimport type { WorkerContext } from '../worker-context.js';\nimport { parseIntSafe } from '../worker-context.js';\nimport { searchObservationsFTS, searchSummariesFiltered, getTimeline } from '../sqlite/Search.js';\nimport { getHybridSearch } from '../search/HybridSearch.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createSearchRouter(ctx: WorkerContext): Router {\n const router = Router();\n\n // FTS5 search with filters\n router.get('/api/search', (req, res) => {\n const { q, project, type, limit } = req.query as { q: string; project?: string; type?: string; limit?: string };\n\n if (!q) {\n res.status(400).json({ error: 'Query parameter \"q\" is required' });\n return;\n }\n\n try {\n const filters = {\n project: project || undefined,\n type: type || undefined,\n limit: parseIntSafe(limit, 20, 1, 100)\n };\n\n const results = {\n observations: searchObservationsFTS(ctx.db.db, q, filters),\n summaries: searchSummariesFiltered(ctx.db.db, q, filters)\n };\n\n res.json(results);\n } catch (error) {\n logger.error('WORKER', 'Search failed', { query: q }, error as Error);\n res.status(500).json({ error: 'Search failed' });\n }\n });\n\n // Hybrid search (vector + keyword)\n router.get('/api/hybrid-search', async (req, res) => {\n const { q, project, limit } = req.query as { q: string; project?: string; limit?: string };\n\n if (!q) {\n res.status(400).json({ error: 'Query parameter \"q\" is required' });\n return;\n }\n\n try {\n const hybridSearch = getHybridSearch();\n const results = await hybridSearch.search(ctx.db.db, q, {\n project: project || undefined,\n limit: parseIntSafe(limit, 10, 1, 100)\n });\n\n res.json({ results, count: results.length });\n } catch (error) {\n logger.error('WORKER', 'Hybrid search failed', { query: q }, error as Error);\n res.status(500).json({ error: 'Hybrid search failed' });\n }\n });\n\n // Timeline: chronological context around an observation\n router.get('/api/timeline', (req, res) => {\n const { anchor, depth_before, depth_after } = req.query as { anchor: string; depth_before?: string; depth_after?: string };\n\n if (!anchor) {\n res.status(400).json({ error: 'Query parameter \"anchor\" is required' });\n return;\n }\n\n const anchorId = parseIntSafe(anchor, 0, 1, Number.MAX_SAFE_INTEGER);\n if (anchorId === 0) {\n res.status(400).json({ error: 'Invalid \"anchor\" (must be positive integer)' });\n return;\n }\n\n try {\n const timeline = getTimeline(\n ctx.db.db,\n anchorId,\n parseIntSafe(depth_before, 5, 1, 50),\n parseIntSafe(depth_after, 5, 1, 50)\n );\n\n res.json({ timeline });\n } catch (error) {\n logger.error('WORKER', 'Timeline failed', { anchor }, error as Error);\n res.status(500).json({ error: 'Timeline failed' });\n }\n });\n\n return router;\n}\n", "/**\n * Router Analytics: overview, timeline, type distribution, sessions.\n */\n\nimport { Router } from 'express';\nimport type { WorkerContext } from '../worker-context.js';\nimport { isValidProject, parseIntSafe } from '../worker-context.js';\nimport { getObservationsTimeline, getTypeDistribution, getSessionStats, getAnalyticsOverview } from '../sqlite/Analytics.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createAnalyticsRouter(ctx: WorkerContext): Router {\n const router = Router();\n\n router.get('/api/analytics/overview', (req, res) => {\n const { project } = req.query as { project?: string };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const overview = getAnalyticsOverview(ctx.db.db, project || undefined);\n res.json(overview);\n } catch (error) {\n logger.error('WORKER', 'Analytics overview failed', { project }, error as Error);\n res.status(500).json({ error: 'Analytics overview failed' });\n }\n });\n\n router.get('/api/analytics/timeline', (req, res) => {\n const { project, days } = req.query as { project?: string; days?: string };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const timeline = getObservationsTimeline(\n ctx.db.db,\n project || undefined,\n parseIntSafe(days, 30, 1, 365)\n );\n res.json(timeline);\n } catch (error) {\n logger.error('WORKER', 'Analytics timeline failed', { project }, error as Error);\n res.status(500).json({ error: 'Analytics timeline failed' });\n }\n });\n\n router.get('/api/analytics/types', (req, res) => {\n const { project } = req.query as { project?: string };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const distribution = getTypeDistribution(ctx.db.db, project || undefined);\n res.json(distribution);\n } catch (error) {\n logger.error('WORKER', 'Analytics types failed', { project }, error as Error);\n res.status(500).json({ error: 'Analytics types failed' });\n }\n });\n\n router.get('/api/analytics/sessions', (req, res) => {\n const { project } = req.query as { project?: string };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const stats = getSessionStats(ctx.db.db, project || undefined);\n res.json(stats);\n } catch (error) {\n logger.error('WORKER', 'Analytics sessions failed', { project }, error as Error);\n res.status(500).json({ error: 'Analytics sessions failed' });\n }\n });\n\n return router;\n}\n", "import { Database } from 'bun:sqlite';\n\n/**\n * Analytics module for Kiro Memory.\n * Aggregate queries for metrics dashboard.\n */\n\n// ============================================================================\n// Return types\n// ============================================================================\n\nexport interface TimelineDayEntry {\n day: string;\n count: number;\n}\n\nexport interface TypeDistributionEntry {\n type: string;\n count: number;\n}\n\nexport interface SessionStatsResult {\n total: number;\n completed: number;\n avgDurationMinutes: number;\n}\n\nexport interface TokenEconomicsResult {\n discoveryTokens: number;\n readTokens: number;\n savings: number;\n reductionPct: number;\n}\n\nexport interface AnalyticsOverviewResult {\n observations: number;\n summaries: number;\n sessions: number;\n prompts: number;\n observationsToday: number;\n observationsThisWeek: number;\n staleCount: number;\n knowledgeCount: number;\n tokenEconomics: TokenEconomicsResult;\n}\n\n// ============================================================================\n// Query functions\n// ============================================================================\n\n/**\n * Observations per day (last N days).\n * Returns array sorted chronologically (oldest to most recent).\n */\nexport function getObservationsTimeline(\n db: Database,\n project?: string,\n days: number = 30\n): TimelineDayEntry[] {\n const cutoffEpoch = Date.now() - (days * 24 * 60 * 60 * 1000);\n\n const sql = project\n ? `SELECT DATE(datetime(created_at_epoch / 1000, 'unixepoch')) as day, COUNT(*) as count\n FROM observations\n WHERE project = ? AND created_at_epoch >= ?\n GROUP BY day\n ORDER BY day ASC`\n : `SELECT DATE(datetime(created_at_epoch / 1000, 'unixepoch')) as day, COUNT(*) as count\n FROM observations\n WHERE created_at_epoch >= ?\n GROUP BY day\n ORDER BY day ASC`;\n\n const stmt = db.query(sql);\n const rows = project\n ? stmt.all(project, cutoffEpoch) as TimelineDayEntry[]\n : stmt.all(cutoffEpoch) as TimelineDayEntry[];\n\n return rows;\n}\n\n/**\n * Observation distribution by type.\n * Returns array sorted by count descending.\n */\nexport function getTypeDistribution(\n db: Database,\n project?: string\n): TypeDistributionEntry[] {\n const sql = project\n ? `SELECT type, COUNT(*) as count\n FROM observations\n WHERE project = ?\n GROUP BY type\n ORDER BY count DESC`\n : `SELECT type, COUNT(*) as count\n FROM observations\n GROUP BY type\n ORDER BY count DESC`;\n\n const stmt = db.query(sql);\n const rows = project\n ? stmt.all(project) as TypeDistributionEntry[]\n : stmt.all() as TypeDistributionEntry[];\n\n return rows;\n}\n\n/**\n * Session statistics: total, completed, average duration.\n * Single query with conditional aggregation replacing 3 separate queries.\n */\nexport function getSessionStats(\n db: Database,\n project?: string\n): SessionStatsResult {\n const projectFilter = project ? 'WHERE project = ?' : '';\n\n const sql = `\n SELECT\n COUNT(*) as total,\n COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed,\n AVG(CASE\n WHEN status = 'completed' AND completed_at_epoch IS NOT NULL AND completed_at_epoch > started_at_epoch\n THEN (completed_at_epoch - started_at_epoch) / 1000.0 / 60.0\n END) as avg_min\n FROM sessions\n ${projectFilter}\n `;\n\n const row = project\n ? db.query(sql).get(project) as any\n : db.query(sql).get() as any;\n\n return {\n total: row?.total || 0,\n completed: row?.completed || 0,\n avgDurationMinutes: Math.round((row?.avg_min || 0) * 10) / 10,\n };\n}\n\n/**\n * General overview: base counts + daily/weekly trends.\n * Single CTE query replacing the 10 separate queries (fix N+1).\n */\nexport function getAnalyticsOverview(\n db: Database,\n project?: string\n): AnalyticsOverviewResult {\n const now = Date.now();\n const todayStart = now - (now % (24 * 60 * 60 * 1000)); // Start of day UTC\n const weekStart = now - (7 * 24 * 60 * 60 * 1000);\n\n // Single CTE query: 10 counts in a single DB roundtrip\n const projectFilter = project ? 'WHERE project = @project' : '';\n const obsProjectFilter = project ? 'WHERE project = @project' : '';\n\n const sql = `\n WITH\n obs_stats AS (\n SELECT\n COUNT(*) as total,\n COUNT(CASE WHEN created_at_epoch >= @todayStart THEN 1 END) as today,\n COUNT(CASE WHEN created_at_epoch >= @weekStart THEN 1 END) as this_week,\n COUNT(CASE WHEN is_stale = 1 THEN 1 END) as stale,\n COUNT(CASE WHEN type IN ('constraint', 'decision', 'heuristic', 'rejected') THEN 1 END) as knowledge,\n COALESCE(SUM(discovery_tokens), 0) as discovery_tokens,\n COALESCE(SUM(CAST((LENGTH(COALESCE(title, '')) + LENGTH(COALESCE(narrative, ''))) / 4 AS INTEGER)), 0) as read_tokens\n FROM observations\n ${obsProjectFilter}\n ),\n sum_count AS (\n SELECT COUNT(*) as total FROM summaries ${projectFilter}\n ),\n sess_count AS (\n SELECT COUNT(*) as total FROM sessions ${projectFilter}\n ),\n prompt_count AS (\n SELECT COUNT(*) as total FROM prompts ${projectFilter}\n )\n SELECT\n obs_stats.total as observations,\n obs_stats.today as observations_today,\n obs_stats.this_week as observations_this_week,\n obs_stats.stale as stale_count,\n obs_stats.knowledge as knowledge_count,\n obs_stats.discovery_tokens,\n obs_stats.read_tokens,\n sum_count.total as summaries,\n sess_count.total as sessions,\n prompt_count.total as prompts\n FROM obs_stats, sum_count, sess_count, prompt_count\n `;\n\n const params: Record<string, any> = {\n '@todayStart': todayStart,\n '@weekStart': weekStart\n };\n if (project) {\n params['@project'] = project;\n }\n\n const row = db.query(sql).get(params) as any;\n\n const discoveryTokens = row?.discovery_tokens || 0;\n const readTokens = row?.read_tokens || 0;\n const savings = Math.max(0, discoveryTokens - readTokens);\n const reductionPct = discoveryTokens > 0 ? Math.round((1 - readTokens / discoveryTokens) * 100) : 0;\n\n return {\n observations: row?.observations || 0,\n summaries: row?.summaries || 0,\n sessions: row?.sessions || 0,\n prompts: row?.prompts || 0,\n observationsToday: row?.observations_today || 0,\n observationsThisWeek: row?.observations_this_week || 0,\n staleCount: row?.stale_count || 0,\n knowledgeCount: row?.knowledge_count || 0,\n tokenEconomics: { discoveryTokens, readTokens, savings, reductionPct }\n };\n}\n", "/**\n * Router Sessions: session list, checkpoints, prompts.\n */\n\nimport { Router } from 'express';\nimport type { WorkerContext } from '../worker-context.js';\nimport { isValidProject, parseIntSafe } from '../worker-context.js';\nimport { getSessionsByProject, getAllSessions } from '../sqlite/Sessions.js';\nimport { getLatestCheckpoint, getLatestCheckpointByProject } from '../sqlite/Checkpoints.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createSessionsRouter(ctx: WorkerContext): Router {\n const router = Router();\n\n // Session list\n router.get('/api/sessions', (req, res) => {\n const { project } = req.query as { project?: string };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const sessions = project\n ? getSessionsByProject(ctx.db.db, project, 50)\n : getAllSessions(ctx.db.db, 50);\n res.json(sessions);\n } catch (error) {\n logger.error('WORKER', 'Session list failed', { project }, error as Error);\n res.status(500).json({ error: 'Sessions list failed' });\n }\n });\n\n // Checkpoint by session\n router.get('/api/sessions/:id/checkpoint', (req, res) => {\n const sessionId = parseInt(req.params.id, 10);\n\n if (isNaN(sessionId) || sessionId <= 0) {\n res.status(400).json({ error: 'Invalid session ID' });\n return;\n }\n\n try {\n const checkpoint = getLatestCheckpoint(ctx.db.db, sessionId);\n if (!checkpoint) {\n res.status(404).json({ error: 'No checkpoint found for this session' });\n return;\n }\n res.json(checkpoint);\n } catch (error) {\n logger.error('WORKER', 'Checkpoint fetch failed', { sessionId }, error as Error);\n res.status(500).json({ error: 'Checkpoint fetch failed' });\n }\n });\n\n // Checkpoint by project\n router.get('/api/checkpoint', (req, res) => {\n const { project } = req.query as { project?: string };\n\n if (!project) {\n res.status(400).json({ error: 'Project parameter is required' });\n return;\n }\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const checkpoint = getLatestCheckpointByProject(ctx.db.db, project);\n if (!checkpoint) {\n res.status(404).json({ error: 'No checkpoint found for this project' });\n return;\n }\n res.json(checkpoint);\n } catch (error) {\n logger.error('WORKER', 'Project checkpoint fetch failed', { project }, error as Error);\n res.status(500).json({ error: 'Project checkpoint fetch failed' });\n }\n });\n\n // Paginated prompt list\n router.get('/api/prompts', (req, res) => {\n const { offset, limit, project } = req.query as { offset?: string; limit?: string; project?: string };\n const _offset = parseIntSafe(offset, 0, 0, 1_000_000);\n const _limit = parseIntSafe(limit, 20, 1, 200);\n\n try {\n const countSql = project\n ? 'SELECT COUNT(*) as total FROM prompts WHERE project = ?'\n : 'SELECT COUNT(*) as total FROM prompts';\n const countStmt = ctx.db.db.query(countSql);\n const { total } = (project ? countStmt.get(project) : countStmt.get()) as { total: number };\n\n const sql = project\n ? 'SELECT * FROM prompts WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ? OFFSET ?'\n : 'SELECT * FROM prompts ORDER BY created_at_epoch DESC LIMIT ? OFFSET ?';\n const stmt = ctx.db.db.query(sql);\n const rows = project ? stmt.all(project, _limit, _offset) : stmt.all(_limit, _offset);\n res.setHeader('X-Total-Count', total);\n res.json(rows);\n } catch (error) {\n logger.error('WORKER', 'Prompt list failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to list prompts' });\n }\n });\n\n return router;\n}\n", "import { Database } from 'bun:sqlite';\nimport type { DBSession } from '../../types/worker-types.js';\n\n/**\n * Session operations for Kiro Memory database\n */\n\nexport function createSession(\n db: Database,\n contentSessionId: string,\n project: string,\n userPrompt: string\n): number {\n const now = new Date();\n const result = db.run(\n `INSERT INTO sessions (content_session_id, project, user_prompt, status, started_at, started_at_epoch)\n VALUES (?, ?, ?, 'active', ?, ?)`,\n [contentSessionId, project, userPrompt, now.toISOString(), now.getTime()]\n );\n return Number(result.lastInsertRowid);\n}\n\nexport function getSessionByContentId(db: Database, contentSessionId: string): DBSession | null {\n const query = db.query('SELECT * FROM sessions WHERE content_session_id = ?');\n return query.get(contentSessionId) as DBSession | null;\n}\n\nexport function getSessionById(db: Database, id: number): DBSession | null {\n const query = db.query('SELECT * FROM sessions WHERE id = ?');\n return query.get(id) as DBSession | null;\n}\n\nexport function updateSessionMemoryId(\n db: Database,\n id: number,\n memorySessionId: string\n): void {\n db.run(\n 'UPDATE sessions SET memory_session_id = ? WHERE id = ?',\n [memorySessionId, id]\n );\n}\n\nexport function completeSession(db: Database, id: number): void {\n const now = new Date();\n db.run(\n `UPDATE sessions \n SET status = 'completed', completed_at = ?, completed_at_epoch = ?\n WHERE id = ?`,\n [now.toISOString(), now.getTime(), id]\n );\n}\n\nexport function failSession(db: Database, id: number): void {\n const now = new Date();\n db.run(\n `UPDATE sessions \n SET status = 'failed', completed_at = ?, completed_at_epoch = ?\n WHERE id = ?`,\n [now.toISOString(), now.getTime(), id]\n );\n}\n\nexport function getActiveSessions(db: Database): DBSession[] {\n const query = db.query('SELECT * FROM sessions WHERE status = \\'active\\' ORDER BY started_at_epoch DESC');\n return query.all() as DBSession[];\n}\n\nexport function getAllSessions(db: Database, limit: number = 100): DBSession[] {\n const query = db.query('SELECT * FROM sessions ORDER BY started_at_epoch DESC LIMIT ?');\n return query.all(limit) as DBSession[];\n}\n\nexport function getSessionsByProject(db: Database, project: string, limit: number = 100): DBSession[] {\n const query = db.query('SELECT * FROM sessions WHERE project = ? ORDER BY started_at_epoch DESC LIMIT ?');\n return query.all(project, limit) as DBSession[];\n}\n", "import { Database } from 'bun:sqlite';\nimport type { DBCheckpoint } from '../../types/worker-types.js';\n\n/**\n * Checkpoint operations per Kiro Memory database.\n * I checkpoint salvano uno snapshot strutturato della sessione per resume futuro.\n */\n\nexport function createCheckpoint(\n db: Database,\n sessionId: number,\n project: string,\n data: {\n task: string;\n progress?: string;\n nextSteps?: string;\n openQuestions?: string;\n relevantFiles?: string;\n contextSnapshot?: string;\n }\n): number {\n const now = new Date();\n const result = db.run(\n `INSERT INTO checkpoints (session_id, project, task, progress, next_steps, open_questions, relevant_files, context_snapshot, created_at, created_at_epoch)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n sessionId,\n project,\n data.task,\n data.progress || null,\n data.nextSteps || null,\n data.openQuestions || null,\n data.relevantFiles || null,\n data.contextSnapshot || null,\n now.toISOString(),\n now.getTime()\n ]\n );\n return Number(result.lastInsertRowid);\n}\n\nexport function getLatestCheckpoint(db: Database, sessionId: number): DBCheckpoint | null {\n const query = db.query(\n 'SELECT * FROM checkpoints WHERE session_id = ? ORDER BY created_at_epoch DESC LIMIT 1'\n );\n return query.get(sessionId) as DBCheckpoint | null;\n}\n\nexport function getLatestCheckpointByProject(db: Database, project: string): DBCheckpoint | null {\n const query = db.query(\n 'SELECT * FROM checkpoints WHERE project = ? ORDER BY created_at_epoch DESC LIMIT 1'\n );\n return query.get(project) as DBCheckpoint | null;\n}\n\nexport function getCheckpointsBySession(db: Database, sessionId: number): DBCheckpoint[] {\n const query = db.query(\n 'SELECT * FROM checkpoints WHERE session_id = ? ORDER BY created_at_epoch DESC'\n );\n return query.all(sessionId) as DBCheckpoint[];\n}\n", "/**\n * Router Projects: project list, aliases, project statistics.\n */\n\nimport { Router } from 'express';\nimport type { WorkerContext } from '../worker-context.js';\nimport { isValidProject } from '../worker-context.js';\nimport { projectsCache, PROJECTS_CACHE_TTL } from '../worker-context.js';\nimport { getProjectStats } from '../sqlite/Search.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createProjectsRouter(ctx: WorkerContext): Router {\n const router = Router();\n\n // Distinct project list (with 60s cache TTL)\n router.get('/api/projects', (_req, res) => {\n try {\n const now = Date.now();\n if (now - projectsCache.ts < PROJECTS_CACHE_TTL && projectsCache.data.length > 0) {\n res.json(projectsCache.data);\n return;\n }\n\n const stmt = ctx.db.db.query(\n `SELECT DISTINCT project FROM (\n SELECT project FROM observations\n UNION\n SELECT project FROM summaries\n UNION\n SELECT project FROM prompts\n ) ORDER BY project ASC`\n );\n const rows = stmt.all() as { project: string }[];\n projectsCache.data = rows.map(r => r.project);\n projectsCache.ts = now;\n res.json(projectsCache.data);\n } catch (error) {\n logger.error('WORKER', 'Project list failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to list projects' });\n }\n });\n\n // GET project aliases\n router.get('/api/project-aliases', (_req, res) => {\n try {\n const stmt = ctx.db.db.query('SELECT project_name, display_name FROM project_aliases');\n const rows = stmt.all() as { project_name: string; display_name: string }[];\n const aliases: Record<string, string> = {};\n for (const row of rows) {\n aliases[row.project_name] = row.display_name;\n }\n res.json(aliases);\n } catch (error) {\n logger.error('WORKER', 'Alias list failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to list project aliases' });\n }\n });\n\n // PUT project alias (create or update)\n router.put('/api/project-aliases/:project', (req, res) => {\n const { project } = req.params;\n const { displayName } = req.body;\n\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n if (!displayName || typeof displayName !== 'string' || displayName.trim().length === 0 || displayName.length > 100) {\n res.status(400).json({ error: 'Field \"displayName\" is required (string, max 100 chars)' });\n return;\n }\n\n try {\n const now = new Date().toISOString();\n const stmt = ctx.db.db.query(`\n INSERT INTO project_aliases (project_name, display_name, created_at, updated_at)\n VALUES (?, ?, ?, ?)\n ON CONFLICT(project_name) DO UPDATE SET display_name = excluded.display_name, updated_at = excluded.updated_at\n `);\n stmt.run(project, displayName.trim(), now, now);\n res.json({ ok: true, project_name: project, display_name: displayName.trim() });\n } catch (error) {\n logger.error('WORKER', 'Alias update failed', { project }, error as Error);\n res.status(500).json({ error: 'Failed to update project alias' });\n }\n });\n\n // Project statistics\n router.get('/api/stats/:project', (req, res) => {\n const { project } = req.params;\n\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const stats = getProjectStats(ctx.db.db, project);\n res.json(stats);\n } catch (error) {\n logger.error('WORKER', 'Stats failed', { project }, error as Error);\n res.status(500).json({ error: 'Stats failed' });\n }\n });\n\n return router;\n}\n", "/**\n * Router Data: embeddings, retention, export, report.\n * Handles heavy data operations and maintenance.\n */\n\nimport { Router } from 'express';\nimport type { WorkerContext } from '../worker-context.js';\nimport { isValidProject, parseIntSafe } from '../worker-context.js';\nimport { getEmbeddingService } from '../search/EmbeddingService.js';\nimport { getVectorSearch } from '../search/VectorSearch.js';\nimport { getReportData } from '../sqlite/Reports.js';\nimport { formatReportMarkdown } from '../report-formatter.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createDataRouter(ctx: WorkerContext, workerToken?: string): Router {\n const router = Router();\n\n /** Middleware: requires X-Worker-Token for destructive endpoints */\n function requireAuth(req: import('express').Request, res: import('express').Response, next: import('express').NextFunction): void {\n if (!workerToken) { next(); return; } // No token configured, skip\n const token = req.headers['x-worker-token'] as string;\n if (token !== workerToken) {\n res.status(401).json({ error: 'Invalid or missing X-Worker-Token' });\n return;\n }\n next();\n }\n\n // \u2500\u2500 Embeddings \u2500\u2500\n\n // Backfill embeddings for observations without embeddings\n router.post('/api/embeddings/backfill', requireAuth, async (req, res) => {\n const { batchSize } = req.body || {};\n\n try {\n const vectorSearch = getVectorSearch();\n const count = await vectorSearch.backfillEmbeddings(\n ctx.db.db,\n parseIntSafe(String(batchSize), 50, 1, 500)\n );\n res.json({ success: true, generated: count });\n } catch (error) {\n logger.error('WORKER', 'Backfill embeddings failed', {}, error as Error);\n res.status(500).json({ error: 'Backfill failed' });\n }\n });\n\n // Embedding statistics\n router.get('/api/embeddings/stats', (_req, res) => {\n try {\n const vectorSearch = getVectorSearch();\n const stats = vectorSearch.getStats(ctx.db.db);\n const embeddingService = getEmbeddingService();\n\n res.json({\n ...stats,\n provider: embeddingService.getProvider(),\n dimensions: embeddingService.getDimensions(),\n available: embeddingService.isAvailable()\n });\n } catch (error) {\n logger.error('WORKER', 'Embedding stats failed', {}, error as Error);\n res.status(500).json({ error: 'Stats failed' });\n }\n });\n\n // \u2500\u2500 Retention Policy \u2500\u2500\n\n router.post('/api/retention/cleanup', requireAuth, (req, res) => {\n const { maxAgeDays, dryRun } = req.body || {};\n const days = parseIntSafe(String(maxAgeDays), 90, 7, 730);\n const threshold = Date.now() - (days * 86_400_000);\n\n try {\n if (dryRun) {\n const obsCount = (ctx.db.db.query('SELECT COUNT(*) as c FROM observations WHERE created_at_epoch < ?').get(threshold) as { c: number }).c;\n const sumCount = (ctx.db.db.query('SELECT COUNT(*) as c FROM summaries WHERE created_at_epoch < ?').get(threshold) as { c: number }).c;\n const promptCount = (ctx.db.db.query('SELECT COUNT(*) as c FROM prompts WHERE created_at_epoch < ?').get(threshold) as { c: number }).c;\n res.json({ dryRun: true, maxAgeDays: days, wouldDelete: { observations: obsCount, summaries: sumCount, prompts: promptCount } });\n return;\n }\n\n const cleanup = ctx.db.db.transaction(() => {\n ctx.db.db.run('DELETE FROM observation_embeddings WHERE observation_id IN (SELECT id FROM observations WHERE created_at_epoch < ?)', [threshold]);\n const obsResult = ctx.db.db.run('DELETE FROM observations WHERE created_at_epoch < ?', [threshold]);\n const sumResult = ctx.db.db.run('DELETE FROM summaries WHERE created_at_epoch < ?', [threshold]);\n const promptResult = ctx.db.db.run('DELETE FROM prompts WHERE created_at_epoch < ?', [threshold]);\n return {\n observations: obsResult.changes,\n summaries: sumResult.changes,\n prompts: promptResult.changes,\n };\n });\n\n const deleted = cleanup();\n ctx.invalidateProjectsCache();\n\n logger.info('WORKER', `Retention cleanup: deleted ${deleted.observations} obs, ${deleted.summaries} sum, ${deleted.prompts} prompts (> ${days}d)`);\n res.json({ success: true, maxAgeDays: days, deleted });\n } catch (error) {\n logger.error('WORKER', 'Retention cleanup failed', { maxAgeDays: days }, error as Error);\n res.status(500).json({ error: 'Retention cleanup failed' });\n }\n });\n\n // \u2500\u2500 Export \u2500\u2500\n\n router.get('/api/export', (req, res) => {\n const { project, format: fmt, type, days } = req.query as {\n project?: string; format?: string; type?: string; days?: string;\n };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n const daysBack = parseIntSafe(days, 30, 1, 365);\n const threshold = Date.now() - (daysBack * 86_400_000);\n\n try {\n let sql = 'SELECT * FROM observations WHERE created_at_epoch > ?';\n const params: (string | number)[] = [threshold];\n if (project) { sql += ' AND project = ?'; params.push(project); }\n if (type) { sql += ' AND type = ?'; params.push(type); }\n sql += ' ORDER BY created_at_epoch DESC LIMIT 1000';\n const observations = ctx.db.db.query(sql).all(...params) as any[];\n\n let sumSql = 'SELECT * FROM summaries WHERE created_at_epoch > ?';\n const sumParams: (string | number)[] = [threshold];\n if (project) { sumSql += ' AND project = ?'; sumParams.push(project); }\n sumSql += ' ORDER BY created_at_epoch DESC LIMIT 100';\n const summaries = ctx.db.db.query(sumSql).all(...sumParams) as any[];\n\n if (fmt === 'markdown' || fmt === 'md') {\n const lines: string[] = [\n `# Kiro Memory Export`,\n `> Project: ${project || 'All'} | Period: ${daysBack} days | Generated: ${new Date().toISOString()}`,\n '',\n `## Observations (${observations.length})`,\n '',\n ];\n\n for (const obs of observations) {\n const date = new Date(obs.created_at_epoch).toISOString().split('T')[0];\n lines.push(`### [${obs.type}] ${obs.title}`);\n lines.push(`- **Date**: ${date} | **Project**: ${obs.project} | **ID**: #${obs.id}`);\n if (obs.narrative) lines.push(`- ${obs.narrative}`);\n if (obs.concepts) lines.push(`- **Concepts**: ${obs.concepts}`);\n lines.push('');\n }\n\n lines.push(`## Summaries (${summaries.length})`, '');\n for (const sum of summaries) {\n const date = new Date(sum.created_at_epoch).toISOString().split('T')[0];\n lines.push(`### Session ${sum.session_id} (${date})`);\n if (sum.request) lines.push(`- **Request**: ${sum.request}`);\n if (sum.completed) lines.push(`- **Completed**: ${sum.completed}`);\n if (sum.next_steps) lines.push(`- **Next steps**: ${sum.next_steps}`);\n lines.push('');\n }\n\n res.type('text/markdown').send(lines.join('\\n'));\n } else {\n res.json({\n meta: { project: project || 'all', daysBack, exportedAt: new Date().toISOString() },\n observations,\n summaries,\n });\n }\n } catch (error) {\n logger.error('WORKER', 'Export failed', { project, fmt }, error as Error);\n res.status(500).json({ error: 'Export failed' });\n }\n });\n\n // \u2500\u2500 Report \u2500\u2500\n\n router.get('/api/report', (req, res) => {\n const { project, period, format } = req.query as {\n project?: string; period?: string; format?: string;\n };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n const validPeriods = ['weekly', 'monthly'];\n const reportPeriod = validPeriods.includes(period || '') ? period : 'weekly';\n const daysBack = reportPeriod === 'monthly' ? 30 : 7;\n const now = Date.now();\n const startEpoch = now - (daysBack * 24 * 60 * 60 * 1000);\n\n try {\n const data = getReportData(ctx.db.db, project || undefined, startEpoch, now);\n\n const outputFormat = format || 'json';\n if (outputFormat === 'markdown' || outputFormat === 'md') {\n res.type('text/markdown').send(formatReportMarkdown(data));\n } else if (outputFormat === 'text') {\n res.type('text/plain').send(JSON.stringify(data, null, 2));\n } else {\n res.json(data);\n }\n } catch (error) {\n logger.error('WORKER', 'Report generation failed', { project, period }, error as Error);\n res.status(500).json({ error: 'Report generation failed' });\n }\n });\n\n return router;\n}\n", "import { Database } from 'bun:sqlite';\nimport type { ReportData } from '../../types/worker-types.js';\n\n/**\n * Report module for Kiro Memory.\n * Aggregates metrics for a specific time range.\n */\n\n/**\n * Generate aggregated data for an activity report.\n * Executes 8 queries on the range [startEpoch, endEpoch].\n */\nexport function getReportData(\n db: Database,\n project: string | undefined,\n startEpoch: number,\n endEpoch: number\n): ReportData {\n // Calculate period\n const startDate = new Date(startEpoch);\n const endDate = new Date(endEpoch);\n const days = Math.ceil((endEpoch - startEpoch) / (24 * 60 * 60 * 1000));\n const label = days <= 7 ? 'Weekly' : days <= 31 ? 'Monthly' : 'Custom';\n\n // Helper for queries with project filter + time range\n const countInRange = (table: string, epochCol: string = 'created_at_epoch'): number => {\n const sql = project\n ? `SELECT COUNT(*) as count FROM ${table} WHERE project = ? AND ${epochCol} >= ? AND ${epochCol} <= ?`\n : `SELECT COUNT(*) as count FROM ${table} WHERE ${epochCol} >= ? AND ${epochCol} <= ?`;\n const stmt = db.query(sql);\n const row = project\n ? stmt.get(project, startEpoch, endEpoch) as any\n : stmt.get(startEpoch, endEpoch) as any;\n return row?.count || 0;\n };\n\n // 1. Base counts in the period\n const observations = countInRange('observations');\n const summaries = countInRange('summaries');\n const prompts = countInRange('prompts');\n // Sessions: use started_at_epoch as reference\n const sessions = countInRange('sessions', 'started_at_epoch');\n\n // 2. Daily timeline\n const timelineSql = project\n ? `SELECT DATE(datetime(created_at_epoch / 1000, 'unixepoch')) as day, COUNT(*) as count\n FROM observations\n WHERE project = ? AND created_at_epoch >= ? AND created_at_epoch <= ?\n GROUP BY day ORDER BY day ASC`\n : `SELECT DATE(datetime(created_at_epoch / 1000, 'unixepoch')) as day, COUNT(*) as count\n FROM observations\n WHERE created_at_epoch >= ? AND created_at_epoch <= ?\n GROUP BY day ORDER BY day ASC`;\n const timelineStmt = db.query(timelineSql);\n const timeline = (project\n ? timelineStmt.all(project, startEpoch, endEpoch)\n : timelineStmt.all(startEpoch, endEpoch)\n ) as Array<{ day: string; count: number }>;\n\n // 3. Distribution by type\n const typeSql = project\n ? `SELECT type, COUNT(*) as count FROM observations\n WHERE project = ? AND created_at_epoch >= ? AND created_at_epoch <= ?\n GROUP BY type ORDER BY count DESC`\n : `SELECT type, COUNT(*) as count FROM observations\n WHERE created_at_epoch >= ? AND created_at_epoch <= ?\n GROUP BY type ORDER BY count DESC`;\n const typeStmt = db.query(typeSql);\n const typeDistribution = (project\n ? typeStmt.all(project, startEpoch, endEpoch)\n : typeStmt.all(startEpoch, endEpoch)\n ) as Array<{ type: string; count: number }>;\n\n // 4. Session stats in the period\n const sessionTotalSql = project\n ? `SELECT COUNT(*) as count FROM sessions WHERE project = ? AND started_at_epoch >= ? AND started_at_epoch <= ?`\n : `SELECT COUNT(*) as count FROM sessions WHERE started_at_epoch >= ? AND started_at_epoch <= ?`;\n const sessionTotal = (project\n ? (db.query(sessionTotalSql).get(project, startEpoch, endEpoch) as any)?.count\n : (db.query(sessionTotalSql).get(startEpoch, endEpoch) as any)?.count\n ) || 0;\n\n const sessionCompletedSql = project\n ? `SELECT COUNT(*) as count FROM sessions WHERE project = ? AND started_at_epoch >= ? AND started_at_epoch <= ? AND status = 'completed'`\n : `SELECT COUNT(*) as count FROM sessions WHERE started_at_epoch >= ? AND started_at_epoch <= ? AND status = 'completed'`;\n const sessionCompleted = (project\n ? (db.query(sessionCompletedSql).get(project, startEpoch, endEpoch) as any)?.count\n : (db.query(sessionCompletedSql).get(startEpoch, endEpoch) as any)?.count\n ) || 0;\n\n const sessionAvgSql = project\n ? `SELECT AVG((completed_at_epoch - started_at_epoch) / 1000.0 / 60.0) as avg_min\n FROM sessions\n WHERE project = ? AND started_at_epoch >= ? AND started_at_epoch <= ?\n AND status = 'completed' AND completed_at_epoch IS NOT NULL AND completed_at_epoch > started_at_epoch`\n : `SELECT AVG((completed_at_epoch - started_at_epoch) / 1000.0 / 60.0) as avg_min\n FROM sessions\n WHERE started_at_epoch >= ? AND started_at_epoch <= ?\n AND status = 'completed' AND completed_at_epoch IS NOT NULL AND completed_at_epoch > started_at_epoch`;\n const avgRow = project\n ? db.query(sessionAvgSql).get(project, startEpoch, endEpoch) as any\n : db.query(sessionAvgSql).get(startEpoch, endEpoch) as any;\n const avgDurationMinutes = Math.round((avgRow?.avg_min || 0) * 10) / 10;\n\n // 5. Knowledge count\n const knowledgeSql = project\n ? `SELECT COUNT(*) as count FROM observations\n WHERE project = ? AND created_at_epoch >= ? AND created_at_epoch <= ?\n AND type IN ('constraint', 'decision', 'heuristic', 'rejected')`\n : `SELECT COUNT(*) as count FROM observations\n WHERE created_at_epoch >= ? AND created_at_epoch <= ?\n AND type IN ('constraint', 'decision', 'heuristic', 'rejected')`;\n const knowledgeCount = (project\n ? (db.query(knowledgeSql).get(project, startEpoch, endEpoch) as any)?.count\n : (db.query(knowledgeSql).get(startEpoch, endEpoch) as any)?.count\n ) || 0;\n\n // 6. Stale count\n const staleSql = project\n ? `SELECT COUNT(*) as count FROM observations\n WHERE project = ? AND created_at_epoch >= ? AND created_at_epoch <= ? AND is_stale = 1`\n : `SELECT COUNT(*) as count FROM observations\n WHERE created_at_epoch >= ? AND created_at_epoch <= ? AND is_stale = 1`;\n const staleCount = (project\n ? (db.query(staleSql).get(project, startEpoch, endEpoch) as any)?.count\n : (db.query(staleSql).get(startEpoch, endEpoch) as any)?.count\n ) || 0;\n\n // 7. Summary contents (learnings, completed, next steps)\n const summarySql = project\n ? `SELECT learned, completed, next_steps FROM summaries\n WHERE project = ? AND created_at_epoch >= ? AND created_at_epoch <= ?\n ORDER BY created_at_epoch DESC`\n : `SELECT learned, completed, next_steps FROM summaries\n WHERE created_at_epoch >= ? AND created_at_epoch <= ?\n ORDER BY created_at_epoch DESC`;\n const summaryRows = (project\n ? db.query(summarySql).all(project, startEpoch, endEpoch)\n : db.query(summarySql).all(startEpoch, endEpoch)\n ) as Array<{ learned: string | null; completed: string | null; next_steps: string | null }>;\n\n const topLearnings: string[] = [];\n const completedTasks: string[] = [];\n const nextStepsArr: string[] = [];\n\n for (const row of summaryRows) {\n if (row.learned) {\n // Split by '; ' if concatenated (hook stop.ts format)\n const parts = row.learned.split('; ').filter(Boolean);\n topLearnings.push(...parts);\n }\n if (row.completed) {\n const parts = row.completed.split('; ').filter(Boolean);\n completedTasks.push(...parts);\n }\n if (row.next_steps) {\n const parts = row.next_steps.split('; ').filter(Boolean);\n nextStepsArr.push(...parts);\n }\n }\n\n // 8. File hotspots (most modified files in the period)\n const filesSql = project\n ? `SELECT files_modified FROM observations\n WHERE project = ? AND created_at_epoch >= ? AND created_at_epoch <= ?\n AND files_modified IS NOT NULL AND files_modified != ''`\n : `SELECT files_modified FROM observations\n WHERE created_at_epoch >= ? AND created_at_epoch <= ?\n AND files_modified IS NOT NULL AND files_modified != ''`;\n const fileRows = (project\n ? db.query(filesSql).all(project, startEpoch, endEpoch)\n : db.query(filesSql).all(startEpoch, endEpoch)\n ) as Array<{ files_modified: string }>;\n\n const fileCounts = new Map<string, number>();\n for (const row of fileRows) {\n const files = row.files_modified.split(',').map(f => f.trim()).filter(Boolean);\n for (const file of files) {\n fileCounts.set(file, (fileCounts.get(file) || 0) + 1);\n }\n }\n\n const fileHotspots = Array.from(fileCounts.entries())\n .map(([file, count]) => ({ file, count }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 15);\n\n return {\n period: {\n start: startDate.toISOString().split('T')[0],\n end: endDate.toISOString().split('T')[0],\n days,\n label\n },\n overview: {\n observations,\n summaries,\n sessions,\n prompts,\n knowledgeCount,\n staleCount\n },\n timeline,\n typeDistribution,\n sessionStats: {\n total: sessionTotal,\n completed: sessionCompleted,\n avgDurationMinutes\n },\n topLearnings: [...new Set(topLearnings)].slice(0, 10),\n completedTasks: [...new Set(completedTasks)].slice(0, 10),\n nextSteps: [...new Set(nextStepsArr)].slice(0, 10),\n fileHotspots\n };\n}\n", "import type { ReportData } from '../types/worker-types.js';\n\n/**\n * Report formatters for Kiro Memory.\n * Three outputs: ANSI text (CLI), markdown (file/sharing), JSON (automations).\n */\n\n// ============================================================================\n// ANSI text format (for CLI)\n// ============================================================================\n\nexport function formatReportText(data: ReportData): string {\n const lines: string[] = [];\n\n // Header\n lines.push('');\n lines.push(` \\x1b[36m\u2550\u2550\u2550 Kiro Memory Report \u2014 ${data.period.label} \u2550\u2550\u2550\\x1b[0m`);\n lines.push(` \\x1b[2m${data.period.start} \u2192 ${data.period.end} (${data.period.days} days)\\x1b[0m`);\n lines.push('');\n\n // Overview\n lines.push(` \\x1b[1mOverview\\x1b[0m`);\n lines.push(` Observations: ${data.overview.observations}`);\n lines.push(` Summaries: ${data.overview.summaries}`);\n lines.push(` Sessions: ${data.overview.sessions}`);\n lines.push(` Prompts: ${data.overview.prompts}`);\n lines.push(` Knowledge: ${data.overview.knowledgeCount}`);\n if (data.overview.staleCount > 0) {\n lines.push(` Stale: ${data.overview.staleCount}`);\n }\n lines.push('');\n\n // Session stats\n if (data.sessionStats.total > 0) {\n const completionPct = data.sessionStats.total > 0\n ? Math.round((data.sessionStats.completed / data.sessionStats.total) * 100)\n : 0;\n lines.push(` \\x1b[1mSessions\\x1b[0m`);\n lines.push(` Total: ${data.sessionStats.total} | Completed: ${data.sessionStats.completed} (${completionPct}%)`);\n if (data.sessionStats.avgDurationMinutes > 0) {\n lines.push(` Avg duration: ${data.sessionStats.avgDurationMinutes} min`);\n }\n lines.push('');\n }\n\n // Timeline (bar chart ASCII)\n if (data.timeline.length > 0) {\n lines.push(` \\x1b[1mTimeline\\x1b[0m`);\n const maxCount = Math.max(...data.timeline.map(t => t.count));\n const maxBarLen = 30;\n for (const entry of data.timeline) {\n const barLen = maxCount > 0 ? Math.round((entry.count / maxCount) * maxBarLen) : 0;\n const bar = '\\x1b[32m' + '\u2593'.repeat(barLen) + '\\x1b[0m';\n const dayShort = entry.day.substring(5); // MM-DD\n lines.push(` ${dayShort} ${bar} ${entry.count}`);\n }\n lines.push('');\n }\n\n // Type distribution\n if (data.typeDistribution.length > 0) {\n lines.push(` \\x1b[1mBy Type\\x1b[0m`);\n for (const entry of data.typeDistribution) {\n lines.push(` ${entry.type.padEnd(16)} ${entry.count}`);\n }\n lines.push('');\n }\n\n // Learnings\n if (data.topLearnings.length > 0) {\n lines.push(` \\x1b[1mKey Learnings\\x1b[0m`);\n for (const learning of data.topLearnings) {\n lines.push(` - ${learning}`);\n }\n lines.push('');\n }\n\n // Completed tasks\n if (data.completedTasks.length > 0) {\n lines.push(` \\x1b[1mCompleted\\x1b[0m`);\n for (const task of data.completedTasks) {\n lines.push(` - ${task}`);\n }\n lines.push('');\n }\n\n // Next steps\n if (data.nextSteps.length > 0) {\n lines.push(` \\x1b[1mNext Steps\\x1b[0m`);\n for (const step of data.nextSteps) {\n lines.push(` - ${step}`);\n }\n lines.push('');\n }\n\n // File hotspots\n if (data.fileHotspots.length > 0) {\n lines.push(` \\x1b[1mFile Hotspots\\x1b[0m`);\n for (const entry of data.fileHotspots.slice(0, 10)) {\n lines.push(` ${entry.file} (${entry.count}x)`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\n// ============================================================================\n// Markdown format (for file/sharing)\n// ============================================================================\n\nexport function formatReportMarkdown(data: ReportData): string {\n const lines: string[] = [];\n\n // Header\n lines.push(`# Kiro Memory Report \u2014 ${data.period.label}`);\n lines.push('');\n lines.push(`**Period**: ${data.period.start} \u2192 ${data.period.end} (${data.period.days} days)`);\n lines.push('');\n\n // Overview table\n lines.push('## Overview');\n lines.push('');\n lines.push('| Metric | Count |');\n lines.push('|--------|------:|');\n lines.push(`| Observations | ${data.overview.observations} |`);\n lines.push(`| Summaries | ${data.overview.summaries} |`);\n lines.push(`| Sessions | ${data.overview.sessions} |`);\n lines.push(`| Prompts | ${data.overview.prompts} |`);\n lines.push(`| Knowledge items | ${data.overview.knowledgeCount} |`);\n if (data.overview.staleCount > 0) {\n lines.push(`| Stale observations | ${data.overview.staleCount} |`);\n }\n lines.push('');\n\n // Sessions\n if (data.sessionStats.total > 0) {\n const completionPct = Math.round((data.sessionStats.completed / data.sessionStats.total) * 100);\n lines.push('## Sessions');\n lines.push('');\n lines.push(`- **Total**: ${data.sessionStats.total}`);\n lines.push(`- **Completed**: ${data.sessionStats.completed} (${completionPct}%)`);\n if (data.sessionStats.avgDurationMinutes > 0) {\n lines.push(`- **Avg duration**: ${data.sessionStats.avgDurationMinutes} min`);\n }\n lines.push('');\n }\n\n // Timeline\n if (data.timeline.length > 0) {\n lines.push('## Activity Timeline');\n lines.push('');\n lines.push('| Date | Observations |');\n lines.push('|------|------------:|');\n for (const entry of data.timeline) {\n lines.push(`| ${entry.day} | ${entry.count} |`);\n }\n lines.push('');\n }\n\n // Type distribution\n if (data.typeDistribution.length > 0) {\n lines.push('## Observation Types');\n lines.push('');\n for (const entry of data.typeDistribution) {\n lines.push(`- **${entry.type}**: ${entry.count}`);\n }\n lines.push('');\n }\n\n // Learnings\n if (data.topLearnings.length > 0) {\n lines.push('## Key Learnings');\n lines.push('');\n for (const learning of data.topLearnings) {\n lines.push(`- ${learning}`);\n }\n lines.push('');\n }\n\n // Completed\n if (data.completedTasks.length > 0) {\n lines.push('## Completed');\n lines.push('');\n for (const task of data.completedTasks) {\n lines.push(`- ${task}`);\n }\n lines.push('');\n }\n\n // Next steps\n if (data.nextSteps.length > 0) {\n lines.push('## Next Steps');\n lines.push('');\n for (const step of data.nextSteps) {\n lines.push(`- ${step}`);\n }\n lines.push('');\n }\n\n // File hotspots\n if (data.fileHotspots.length > 0) {\n lines.push('## File Hotspots');\n lines.push('');\n lines.push('| File | Modifications |');\n lines.push('|------|-------------:|');\n for (const entry of data.fileHotspots.slice(0, 10)) {\n lines.push(`| \\`${entry.file}\\` | ${entry.count} |`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\n// ============================================================================\n// JSON format (for automations/webhooks)\n// ============================================================================\n\nexport function formatReportJson(data: ReportData): string {\n return JSON.stringify(data, null, 2);\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["exports", "isInSubnet", "isCorrect", "numberToPaddedHex", "stringToPaddedHex", "testBit", "address", "defaultBits", "number", "numberString", "binaryValue", "position", "length", "positionInString", "exports", "AddressError", "message", "parseMessage", "exports", "common", "__importStar", "constants", "address_error_1", "Address4", "_Address4", "address", "subnet", "groups", "part", "hex", "padded", "i", "h", "integer", "arpaFormAddress", "output", "n", "adjust", "bigInt", "mask", "start", "end", "options", "reversed", "segments", "exports", "exports", "exports", "spanAllZeroes", "spanAll", "spanLeadingZeroes", "simpleGroup", "s", "offset", "n", "i", "spanLeadingZeroesSimple", "group", "address", "g", "addressString", "exports", "groupPossibilities", "padGroup", "simpleRegularExpression", "possibleElisions", "v6", "__importStar", "possibilities", "group", "groups", "zeroIndexes", "i", "zeroIndex", "elision", "elidedGroups", "moreLeft", "moreRight", "left", "right", "position", "common", "__importStar", "constants4", "constants6", "helpers", "ipv4_1", "regular_expressions_1", "address_error_1", "common_1", "assert", "condition", "addCommas", "number", "r", "spanLeadingZeroes4", "n", "compact", "address", "slice", "s1", "s2", "i", "paddedHex", "octet", "unsignByte", "b", "Address6", "_Address6", "optionalGroups", "subnet", "zone", "bigInt", "hex", "groups", "url", "host", "port", "result", "address4", "mask6", "arpaFormAddress", "semicolonAmount", "parts", "insertIndex", "mask", "subnetSize", "availableBits", "subnetBits", "subnetPowers", "adjust", "scope", "start", "end", "length", "options", "characters", "reversed", "zeroCounter", "zeroes", "value", "zeroLengths", "index", "correct", "badCharacters", "badAddress", "halves", "first", "last", "remaining", "group", "binary", "infix", "prefix", "udpPort", "server4", "bitsForClient4", "client4", "flagsBase2", "coneNat", "reserved", "groupIndividual", "universalLocal", "nonce", "gateway", "addr6to4", "valueWithoutPadding", "bytes", "BYTE_MAX", "multiplier", "optionalPort", "formFunction", "form", "output", "left", "right", "classes", "substringSearch", "address6", "exports", "ipv4_1", "exports", "ipv6_1", "address_error_1", "helpers", "__importStar", "Search_exports", "__export", "getObservationsByIds", "getProjectStats", "getStaleObservations", "getTimeline", "markObservationsStale", "searchObservationsFTS", "searchObservationsFTSWithRank", "searchObservationsLIKE", "searchSummariesFiltered", "existsSync", "statSync", "escapeLikePattern", "input", "sanitizeFTS5Query", "query", "t", "db", "filters", "limit", "safeQuery", "sql", "params", "BM25_WEIGHTS", "pattern", "ids", "validIds", "id", "anchorId", "depthBefore", "depthAfter", "anchor", "anchorEpoch", "before", "self", "after", "project", "row", "discoveryTokens", "readTokens", "savings", "rows", "staleObs", "obs", "files", "f", "isStale", "filepath", "stale", "placeholders", "init_Search", "__esmMin", "Observations_exports", "__export", "consolidateObservations", "createObservation", "deleteObservation", "getObservationsByProject", "getObservationsBySession", "isDuplicateObservation", "searchObservations", "updateLastAccessed", "escapeLikePattern", "input", "db", "contentHash", "windowMs", "threshold", "memorySessionId", "project", "type", "title", "subtitle", "text", "narrative", "facts", "concepts", "filesRead", "filesModified", "promptNumber", "discoveryTokens", "now", "result", "limit", "searchTerm", "sql", "pattern", "query", "id", "ids", "validIds", "placeholders", "options", "minGroupSize", "groups", "totalMerged", "totalRemoved", "group", "obsIds", "count", "merged", "removed", "observations", "keeper", "others", "uniqueTexts", "obs", "consolidatedText", "removeIds", "o", "removePlaceholders", "init_Observations", "__esmMin", "express", "cors", "dangerouslyDisableDefaultSrc", "SHOULD_BE_QUOTED", "getDefaultDirectives", "dashify", "str", "capitalLetter", "assertDirectiveValueIsValid", "directiveName", "directiveValue", "assertDirectiveValueEntryIsValid", "directiveValueEntry", "normalizeDirectives", "options", "defaultDirectives", "useDefaults", "rawDirectives", "result", "directiveNamesSeen", "directivesExplicitlyDisabled", "rawDirectiveName", "rawDirectiveValue", "element", "defaultDirectiveName", "defaultDirectiveValue", "getHeaderValue", "req", "res", "normalizedDirectives", "newElement", "contentSecurityPolicy", "headerName", "next", "ALLOWED_POLICIES$2", "getHeaderValueFromOptions$6", "policy", "crossOriginEmbedderPolicy", "headerValue", "_req", "ALLOWED_POLICIES$1", "getHeaderValueFromOptions$5", "crossOriginOpenerPolicy", "ALLOWED_POLICIES", "getHeaderValueFromOptions$4", "crossOriginResourcePolicy", "originAgentCluster", "ALLOWED_TOKENS", "getHeaderValueFromOptions$3", "tokens", "tokensSeen", "token", "referrerPolicy", "DEFAULT_MAX_AGE", "parseMaxAge", "value", "getHeaderValueFromOptions$2", "directives", "strictTransportSecurity", "xContentTypeOptions", "xDnsPrefetchControl", "xDownloadOptions", "getHeaderValueFromOptions$1", "action", "normalizedAction", "xFrameOptions", "ALLOWED_PERMITTED_POLICIES", "getHeaderValueFromOptions", "permittedPolicies", "xPermittedCrossDomainPolicies", "xPoweredBy", "xXssProtection", "getMiddlewareFunctionsFromOptions", "strictTransportSecurityOption", "xDnsPrefetchControlOption", "xFrameOptionsOption", "xPermittedCrossDomainPoliciesOption", "helmet", "middlewareFunctions", "middlewareIndex", "internalNext", "err", "middlewareFunction", "import_ip_address", "isIPv6", "isIPv62", "Buffer", "createHash", "isIP", "ipKeyGenerator", "ip", "ipv6Subnet", "MemoryStore", "validations2", "options", "key", "client", "now", "SUPPORTED_DRAFT_VERSIONS", "getResetSeconds", "windowMs", "resetTime", "resetSeconds", "deltaSeconds", "getPartitionKey", "hash", "partitionKey", "setLegacyHeaders", "response", "info", "setDraft6Headers", "windowSeconds", "setDraft7Headers", "setDraft8Headers", "name", "header", "policy", "setRetryAfterHeader", "omitUndefinedProperties", "passedOptions", "omittedOptions", "k", "ValidationError", "code", "message", "url", "ChangeWarning", "usedStores", "singleCountKeys", "validations", "request", "hits", "store", "maybeUniquePrefix", "storeKeys", "storeKey", "keys", "prefixedKey", "limit", "draft_polli_ratelimit_headers", "onLimitReached", "version", "versionString", "validOptions", "supportedValidations", "stack", "keyGenerator", "src", "getValidations", "_enabled", "enabled", "wrappedValidations", "validation", "args", "error", "isLegacyStore", "promisifyStore", "passedStore", "legacyStore", "PromisifiedStore", "resolve", "reject", "totalHits", "getOptionsFromConfig", "config", "directlyPassableEntries", "parseOptions", "notUndefinedOptions", "standardHeaders", "_response", "duration", "property", "seconds", "minutes", "hours", "days", "_request", "subnet", "_next", "_optionsUsed", "handleAsyncErrors", "fn", "next", "rateLimit", "middleware", "augmentedRequest", "incrementResult", "decremented", "decrementKey", "getThrowFn", "rate_limit_default", "crypto", "join", "dirname", "existsSync", "mkdirSync", "writeFileSync", "unlinkSync", "chmodSync", "fileURLToPath", "BetterSqlite3", "Database", "path", "options", "sql", "params", "stmt", "cached", "BunQueryCompat", "fn", "db", "join", "dirname", "basename", "homedir", "existsSync", "mkdirSync", "fileURLToPath", "appendFileSync", "existsSync", "mkdirSync", "readFileSync", "join", "homedir", "LogLevel", "DEFAULT_DATA_DIR", "Logger", "logsDir", "date", "error", "settingsPath", "settingsData", "settings", "envLevel", "sessionId", "observationNum", "data", "keys", "year", "month", "day", "hours", "minutes", "seconds", "ms", "level", "component", "message", "context", "timestamp", "levelStr", "componentStr", "correlationStr", "dataStr", "contextStr", "memorySessionId", "correlationId", "rest", "k", "v", "logLine", "durationMs", "fallback", "callerMatch", "location", "enhancedContext", "logger", "getDirname", "dirname", "fileURLToPath", "_dirname", "_legacyDir", "join", "homedir", "_defaultDir", "existsSync", "DATA_DIR", "KIRO_CONFIG_DIR", "PLUGIN_ROOT", "ARCHIVES_DIR", "LOGS_DIR", "TRASH_DIR", "BACKUPS_DIR", "MODES_DIR", "USER_SETTINGS_PATH", "_legacyDb", "DB_PATH", "VECTOR_DB_DIR", "OBSERVER_SESSIONS_DIR", "KIRO_SETTINGS_PATH", "KIRO_CONTEXT_PATH", "ensureDir", "dirPath", "mkdirSync", "SQLITE_MMAP_SIZE_BYTES", "SQLITE_CACHE_SIZE_PAGES", "KiroMemoryDatabase", "dbPath", "DB_PATH", "skipMigrations", "ensureDir", "DATA_DIR", "Database", "MigrationRunner", "sql", "params", "fn", "db", "currentVersion", "migrations", "migration", "logger", "EmbeddingService", "result", "fastembed", "EmbeddingModel", "FlagEmbedding", "logger", "error", "transformers", "pipeline", "text", "truncated", "texts", "t", "results", "embeddings", "batch", "vec", "output", "dims", "data", "offset", "embedding", "embeddingService", "getEmbeddingService", "DEFAULT_MAX_CANDIDATES", "cosineSimilarity", "a", "b", "len", "dotProduct", "normA", "normB", "i", "ai", "bi", "denominator", "float32ToBuffer", "arr", "bufferToFloat32", "buf", "arrayBuffer", "VectorSearch", "db", "queryEmbedding", "options", "limit", "threshold", "maxCandidates", "conditions", "params", "sql", "rows", "scored", "row", "embedding", "similarity", "logger", "error", "observationId", "model", "blob", "batchSize", "embeddingService", "getEmbeddingService", "count", "parts", "fullText", "totalRow", "embeddedRow", "total", "embedded", "percentage", "vectorSearch", "getVectorSearch", "SEARCH_WEIGHTS", "recencyScore", "createdAtEpoch", "halfLifeHours", "ageMs", "ageHours", "normalizeFTS5Rank", "rank", "allRanks", "minRank", "maxRank", "projectMatchScore", "itemProject", "targetProject", "computeCompositeScore", "signals", "weights", "KNOWLEDGE_TYPE_BOOST", "knowledgeTypeBoost", "type", "HybridSearch", "embeddingService", "getEmbeddingService", "logger", "error", "db", "query", "options", "limit", "weights", "SEARCH_WEIGHTS", "targetProject", "rawItems", "queryEmbedding", "vectorResults", "getVectorSearch", "hit", "searchObservationsFTSWithRank", "keywordResults", "obs", "id", "existing", "allFTS5Ranks", "item", "scored", "signals", "normalizeFTS5Rank", "recencyScore", "projectMatchScore", "score", "computeCompositeScore", "isHybrid", "finalScore", "knowledgeTypeBoost", "a", "b", "finalResults", "updateLastAccessed", "ids", "r", "hybridSearch", "getHybridSearch", "MAX_SSE_CLIENTS", "clients", "getClients", "getMaxSSEClients", "addClient", "res", "removeClient", "index", "broadcast", "event", "data", "message", "client", "err", "logger", "projectsCache", "PROJECTS_CACHE_TTL", "invalidateProjectsCache", "parseIntSafe", "value", "defaultVal", "min", "max", "parsed", "isValidProject", "project", "isValidString", "val", "maxLen", "generateEmbeddingForObservation", "db", "observationId", "title", "content", "concepts", "embeddingService", "getEmbeddingService", "parts", "fullText", "embedding", "getVectorSearch", "error", "createWorkerContext", "id", "Router", "ALLOWED_EVENTS", "createCoreRouter", "ctx", "workerToken", "router", "Router", "notifyLimiter", "rate_limit_default", "req", "res", "event", "data", "_req", "clients", "getClients", "getMaxSSEClients", "addClient", "logger", "keepaliveInterval", "removeClient", "Router", "init_Observations", "createSummary", "db", "sessionId", "project", "request", "investigated", "learned", "completed", "nextSteps", "notes", "now", "result", "getSummariesByProject", "db", "project", "limit", "init_Search", "KNOWLEDGE_TYPES", "createObservationsRouter", "ctx", "router", "Router", "req", "res", "offset", "limit", "project", "_offset", "parseIntSafe", "_limit", "countSql", "countStmt", "total", "sql", "stmt", "rows", "error", "logger", "memorySessionId", "type", "title", "content", "concepts", "files", "isValidProject", "isValidString", "id", "createObservation", "ids", "observations", "getObservationsByIds", "knowledge_type", "severity", "alternatives", "reason", "metaContext", "confidence", "KNOWLEDGE_TYPES", "metadata", "obsType", "conceptStr", "context", "getObservationsByProject", "getSummariesByProject", "Router", "createSummariesRouter", "ctx", "router", "Router", "req", "res", "offset", "limit", "project", "_offset", "parseIntSafe", "_limit", "countSql", "countStmt", "total", "sql", "stmt", "rows", "error", "logger", "sessionId", "request", "learned", "completed", "nextSteps", "isValidProject", "MAX_FIELD", "isValidString", "id", "createSummary", "Router", "init_Search", "createSearchRouter", "ctx", "router", "Router", "req", "res", "q", "project", "type", "limit", "filters", "parseIntSafe", "results", "searchObservationsFTS", "searchSummariesFiltered", "error", "logger", "getHybridSearch", "anchor", "depth_before", "depth_after", "anchorId", "timeline", "getTimeline", "Router", "getObservationsTimeline", "db", "project", "days", "cutoffEpoch", "sql", "stmt", "getTypeDistribution", "getSessionStats", "row", "getAnalyticsOverview", "now", "todayStart", "weekStart", "projectFilter", "params", "discoveryTokens", "readTokens", "savings", "reductionPct", "createAnalyticsRouter", "ctx", "router", "Router", "req", "res", "project", "isValidProject", "overview", "getAnalyticsOverview", "error", "logger", "days", "timeline", "getObservationsTimeline", "parseIntSafe", "distribution", "getTypeDistribution", "stats", "getSessionStats", "Router", "getAllSessions", "db", "limit", "getSessionsByProject", "project", "getLatestCheckpoint", "db", "sessionId", "getLatestCheckpointByProject", "project", "createSessionsRouter", "ctx", "router", "Router", "req", "res", "project", "isValidProject", "sessions", "getSessionsByProject", "getAllSessions", "error", "logger", "sessionId", "checkpoint", "getLatestCheckpoint", "getLatestCheckpointByProject", "offset", "limit", "_offset", "parseIntSafe", "_limit", "countSql", "countStmt", "total", "sql", "stmt", "rows", "Router", "init_Search", "createProjectsRouter", "ctx", "router", "Router", "_req", "res", "now", "projectsCache", "PROJECTS_CACHE_TTL", "rows", "r", "error", "logger", "aliases", "row", "req", "project", "displayName", "isValidProject", "stats", "getProjectStats", "Router", "getReportData", "db", "project", "startEpoch", "endEpoch", "startDate", "endDate", "days", "label", "countInRange", "table", "epochCol", "sql", "stmt", "observations", "summaries", "prompts", "sessions", "timelineSql", "timelineStmt", "timeline", "typeSql", "typeStmt", "typeDistribution", "sessionTotalSql", "sessionTotal", "sessionCompletedSql", "sessionCompleted", "sessionAvgSql", "avgRow", "avgDurationMinutes", "knowledgeSql", "knowledgeCount", "staleSql", "staleCount", "summarySql", "summaryRows", "topLearnings", "completedTasks", "nextStepsArr", "row", "parts", "filesSql", "fileRows", "fileCounts", "files", "f", "file", "fileHotspots", "count", "a", "b", "formatReportMarkdown", "data", "lines", "completionPct", "entry", "learning", "task", "step", "createDataRouter", "ctx", "workerToken", "router", "Router", "requireAuth", "req", "res", "next", "batchSize", "count", "getVectorSearch", "parseIntSafe", "error", "logger", "_req", "stats", "embeddingService", "getEmbeddingService", "maxAgeDays", "dryRun", "days", "threshold", "obsCount", "sumCount", "promptCount", "deleted", "obsResult", "sumResult", "promptResult", "project", "fmt", "type", "isValidProject", "daysBack", "sql", "params", "observations", "sumSql", "sumParams", "summaries", "lines", "obs", "date", "sum", "period", "format", "now", "startEpoch", "data", "getReportData", "outputFormat", "formatReportMarkdown", "__worker_dirname", "dirname", "fileURLToPath", "PORT", "HOST", "PID_FILE", "join", "DATA_DIR", "TOKEN_FILE", "existsSync", "mkdirSync", "WORKER_TOKEN", "crypto", "writeFileSync", "chmodSync", "err", "logger", "db", "KiroMemoryDatabase", "getHybridSearch", "ctx", "createWorkerContext", "app", "express", "helmet", "cors", "rate_limit_default", "createCoreRouter", "createObservationsRouter", "createSummariesRouter", "createSearchRouter", "createAnalyticsRouter", "createSessionsRouter", "createProjectsRouter", "createDataRouter", "_req", "res", "viewerPath", "server", "shutdown", "signal", "sseClients", "getClients", "client", "forceTimeout", "unlinkSync", "error", "reason"]
|
|
3
|
+
"sources": ["../../node_modules/ip-address/src/common.ts", "../../node_modules/ip-address/src/v4/constants.ts", "../../node_modules/ip-address/src/address-error.ts", "../../node_modules/ip-address/src/ipv4.ts", "../../node_modules/ip-address/src/v6/constants.ts", "../../node_modules/ip-address/src/v6/helpers.ts", "../../node_modules/ip-address/src/v6/regular-expressions.ts", "../../node_modules/ip-address/src/ipv6.ts", "../../node_modules/ip-address/src/ip-address.ts", "../../src/services/sqlite/Search.ts", "../../src/utils/secrets.ts", "../../src/utils/categorizer.ts", "../../src/services/sqlite/Observations.ts", "../../src/services/worker-service.ts", "../../node_modules/helmet/index.mjs", "../../node_modules/express-rate-limit/dist/index.mjs", "../../src/shims/bun-sqlite.ts", "../../src/shared/paths.ts", "../../src/utils/logger.ts", "../../src/services/sqlite/Database.ts", "../../src/services/search/EmbeddingService.ts", "../../src/services/search/VectorSearch.ts", "../../src/services/search/ScoringEngine.ts", "../../src/services/search/HybridSearch.ts", "../../src/services/worker-context.ts", "../../src/types/worker-types.ts", "../../src/services/sqlite/Retention.ts", "../../src/services/sqlite/Backup.ts", "../../src/cli/cli-utils.ts", "../../src/services/plugins/PluginLoader.ts", "../../src/services/plugins/PluginRegistry.ts", "../../src/services/routes/core.ts", "../../src/services/sqlite/cursor.ts", "../../src/services/sqlite/Sessions.ts", "../../src/services/sqlite/index.ts", "../../src/services/sqlite/Summaries.ts", "../../src/services/sqlite/Prompts.ts", "../../src/services/sqlite/Checkpoints.ts", "../../src/services/sqlite/Reports.ts", "../../src/services/sqlite/GithubLinks.ts", "../../src/services/sqlite/ImportExport.ts", "../../src/sdk/index.ts", "../../src/index.ts", "../../src/hooks/utils.ts", "../../src/services/routes/observations.ts", "../../src/services/routes/summaries.ts", "../../src/services/routes/search.ts", "../../src/services/routes/analytics.ts", "../../src/services/sqlite/Analytics.ts", "../../src/services/analytics/AnomalyDetector.ts", "../../src/services/routes/sessions.ts", "../../src/services/routes/projects.ts", "../../src/services/routes/data.ts", "../../src/services/report-formatter.ts", "../../src/services/routes/webhooks.ts", "../../src/services/routes/importexport.ts", "../../src/services/openapi/index.ts", "../../src/services/openapi/spec.ts", "../../src/services/routes/backup.ts", "../../src/services/routes/plugins.ts"],
|
|
4
|
+
"sourcesContent": ["import { Address4 } from './ipv4';\nimport { Address6 } from './ipv6';\n\nexport interface ReverseFormOptions {\n omitSuffix?: boolean;\n}\n\nexport function isInSubnet(this: Address4 | Address6, address: Address4 | Address6) {\n if (this.subnetMask < address.subnetMask) {\n return false;\n }\n\n if (this.mask(address.subnetMask) === address.mask()) {\n return true;\n }\n\n return false;\n}\n\nexport function isCorrect(defaultBits: number) {\n return function (this: Address4 | Address6) {\n if (this.addressMinusSuffix !== this.correctForm()) {\n return false;\n }\n\n if (this.subnetMask === defaultBits && !this.parsedSubnet) {\n return true;\n }\n\n return this.parsedSubnet === String(this.subnetMask);\n };\n}\n\nexport function numberToPaddedHex(number: number) {\n return number.toString(16).padStart(2, '0');\n}\n\nexport function stringToPaddedHex(numberString: string) {\n return numberToPaddedHex(parseInt(numberString, 10));\n}\n\n/**\n * @param binaryValue Binary representation of a value (e.g. `10`)\n * @param position Byte position, where 0 is the least significant bit\n */\nexport function testBit(binaryValue: string, position: number): boolean {\n const { length } = binaryValue;\n\n if (position > length) {\n return false;\n }\n\n const positionInString = length - position;\n return binaryValue.substring(positionInString, positionInString + 1) === '1';\n}\n", "export const BITS = 32;\nexport const GROUPS = 4;\n\nexport const RE_ADDRESS =\n /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/g;\n\nexport const RE_SUBNET_STRING = /\\/\\d{1,2}$/;\n", "export class AddressError extends Error {\n parseMessage?: string;\n\n constructor(message: string, parseMessage?: string) {\n super(message);\n\n this.name = 'AddressError';\n\n this.parseMessage = parseMessage;\n }\n}\n", "/* eslint-disable no-param-reassign */\n\nimport * as common from './common';\nimport * as constants from './v4/constants';\nimport { AddressError } from './address-error';\n\n/**\n * Represents an IPv4 address\n * @class Address4\n * @param {string} address - An IPv4 address string\n */\nexport class Address4 {\n address: string;\n addressMinusSuffix?: string;\n groups: number = constants.GROUPS;\n parsedAddress: string[] = [];\n parsedSubnet: string = '';\n subnet: string = '/32';\n subnetMask: number = 32;\n v4: boolean = true;\n\n constructor(address: string) {\n this.address = address;\n\n const subnet = constants.RE_SUBNET_STRING.exec(address);\n\n if (subnet) {\n this.parsedSubnet = subnet[0].replace('/', '');\n this.subnetMask = parseInt(this.parsedSubnet, 10);\n this.subnet = `/${this.subnetMask}`;\n\n if (this.subnetMask < 0 || this.subnetMask > constants.BITS) {\n throw new AddressError('Invalid subnet mask.');\n }\n\n address = address.replace(constants.RE_SUBNET_STRING, '');\n }\n\n this.addressMinusSuffix = address;\n\n this.parsedAddress = this.parse(address);\n }\n\n static isValid(address: string): boolean {\n try {\n // eslint-disable-next-line no-new\n new Address4(address);\n\n return true;\n } catch (e) {\n return false;\n }\n }\n\n /*\n * Parses a v4 address\n */\n parse(address: string) {\n const groups = address.split('.');\n\n if (!address.match(constants.RE_ADDRESS)) {\n throw new AddressError('Invalid IPv4 address.');\n }\n\n return groups;\n }\n\n /**\n * Returns the correct form of an address\n * @memberof Address4\n * @instance\n * @returns {String}\n */\n correctForm(): string {\n return this.parsedAddress.map((part) => parseInt(part, 10)).join('.');\n }\n\n /**\n * Returns true if the address is correct, false otherwise\n * @memberof Address4\n * @instance\n * @returns {Boolean}\n */\n isCorrect = common.isCorrect(constants.BITS);\n\n /**\n * Converts a hex string to an IPv4 address object\n * @memberof Address4\n * @static\n * @param {string} hex - a hex string to convert\n * @returns {Address4}\n */\n static fromHex(hex: string): Address4 {\n const padded = hex.replace(/:/g, '').padStart(8, '0');\n const groups = [];\n let i;\n\n for (i = 0; i < 8; i += 2) {\n const h = padded.slice(i, i + 2);\n\n groups.push(parseInt(h, 16));\n }\n\n return new Address4(groups.join('.'));\n }\n\n /**\n * Converts an integer into a IPv4 address object\n * @memberof Address4\n * @static\n * @param {integer} integer - a number to convert\n * @returns {Address4}\n */\n static fromInteger(integer: number): Address4 {\n return Address4.fromHex(integer.toString(16));\n }\n\n /**\n * Return an address from in-addr.arpa form\n * @memberof Address4\n * @static\n * @param {string} arpaFormAddress - an 'in-addr.arpa' form ipv4 address\n * @returns {Adress4}\n * @example\n * var address = Address4.fromArpa(42.2.0.192.in-addr.arpa.)\n * address.correctForm(); // '192.0.2.42'\n */\n static fromArpa(arpaFormAddress: string): Address4 {\n // remove ending \".in-addr.arpa.\" or just \".\"\n const leader = arpaFormAddress.replace(/(\\.in-addr\\.arpa)?\\.$/, '');\n\n const address = leader.split('.').reverse().join('.');\n\n return new Address4(address);\n }\n\n /**\n * Converts an IPv4 address object to a hex string\n * @memberof Address4\n * @instance\n * @returns {String}\n */\n toHex(): string {\n return this.parsedAddress.map((part) => common.stringToPaddedHex(part)).join(':');\n }\n\n /**\n * Converts an IPv4 address object to an array of bytes\n * @memberof Address4\n * @instance\n * @returns {Array}\n */\n toArray(): number[] {\n return this.parsedAddress.map((part) => parseInt(part, 10));\n }\n\n /**\n * Converts an IPv4 address object to an IPv6 address group\n * @memberof Address4\n * @instance\n * @returns {String}\n */\n toGroup6(): string {\n const output = [];\n let i;\n\n for (i = 0; i < constants.GROUPS; i += 2) {\n output.push(\n `${common.stringToPaddedHex(this.parsedAddress[i])}${common.stringToPaddedHex(\n this.parsedAddress[i + 1],\n )}`,\n );\n }\n\n return output.join(':');\n }\n\n /**\n * Returns the address as a `bigint`\n * @memberof Address4\n * @instance\n * @returns {bigint}\n */\n bigInt(): bigint {\n return BigInt(`0x${this.parsedAddress.map((n) => common.stringToPaddedHex(n)).join('')}`);\n }\n\n /**\n * Helper function getting start address.\n * @memberof Address4\n * @instance\n * @returns {bigint}\n */\n _startAddress(): bigint {\n return BigInt(`0b${this.mask() + '0'.repeat(constants.BITS - this.subnetMask)}`);\n }\n\n /**\n * The first address in the range given by this address' subnet.\n * Often referred to as the Network Address.\n * @memberof Address4\n * @instance\n * @returns {Address4}\n */\n startAddress(): Address4 {\n return Address4.fromBigInt(this._startAddress());\n }\n\n /**\n * The first host address in the range given by this address's subnet ie\n * the first address after the Network Address\n * @memberof Address4\n * @instance\n * @returns {Address4}\n */\n startAddressExclusive(): Address4 {\n const adjust = BigInt('1');\n return Address4.fromBigInt(this._startAddress() + adjust);\n }\n\n /**\n * Helper function getting end address.\n * @memberof Address4\n * @instance\n * @returns {bigint}\n */\n _endAddress(): bigint {\n return BigInt(`0b${this.mask() + '1'.repeat(constants.BITS - this.subnetMask)}`);\n }\n\n /**\n * The last address in the range given by this address' subnet\n * Often referred to as the Broadcast\n * @memberof Address4\n * @instance\n * @returns {Address4}\n */\n endAddress(): Address4 {\n return Address4.fromBigInt(this._endAddress());\n }\n\n /**\n * The last host address in the range given by this address's subnet ie\n * the last address prior to the Broadcast Address\n * @memberof Address4\n * @instance\n * @returns {Address4}\n */\n endAddressExclusive(): Address4 {\n const adjust = BigInt('1');\n return Address4.fromBigInt(this._endAddress() - adjust);\n }\n\n /**\n * Converts a BigInt to a v4 address object\n * @memberof Address4\n * @static\n * @param {bigint} bigInt - a BigInt to convert\n * @returns {Address4}\n */\n static fromBigInt(bigInt: bigint): Address4 {\n return Address4.fromHex(bigInt.toString(16));\n }\n\n /**\n * Returns the first n bits of the address, defaulting to the\n * subnet mask\n * @memberof Address4\n * @instance\n * @returns {String}\n */\n mask(mask?: number): string {\n if (mask === undefined) {\n mask = this.subnetMask;\n }\n\n return this.getBitsBase2(0, mask);\n }\n\n /**\n * Returns the bits in the given range as a base-2 string\n * @memberof Address4\n * @instance\n * @returns {string}\n */\n getBitsBase2(start: number, end: number): string {\n return this.binaryZeroPad().slice(start, end);\n }\n\n /**\n * Return the reversed ip6.arpa form of the address\n * @memberof Address4\n * @param {Object} options\n * @param {boolean} options.omitSuffix - omit the \"in-addr.arpa\" suffix\n * @instance\n * @returns {String}\n */\n reverseForm(options?: common.ReverseFormOptions): string {\n if (!options) {\n options = {};\n }\n\n const reversed = this.correctForm().split('.').reverse().join('.');\n\n if (options.omitSuffix) {\n return reversed;\n }\n\n return `${reversed}.in-addr.arpa.`;\n }\n\n /**\n * Returns true if the given address is in the subnet of the current address\n * @memberof Address4\n * @instance\n * @returns {boolean}\n */\n isInSubnet = common.isInSubnet;\n\n /**\n * Returns true if the given address is a multicast address\n * @memberof Address4\n * @instance\n * @returns {boolean}\n */\n isMulticast(): boolean {\n return this.isInSubnet(new Address4('224.0.0.0/4'));\n }\n\n /**\n * Returns a zero-padded base-2 string representation of the address\n * @memberof Address4\n * @instance\n * @returns {string}\n */\n binaryZeroPad(): string {\n return this.bigInt().toString(2).padStart(constants.BITS, '0');\n }\n\n /**\n * Groups an IPv4 address for inclusion at the end of an IPv6 address\n * @returns {String}\n */\n groupForV6(): string {\n const segments = this.parsedAddress;\n\n return this.address.replace(\n constants.RE_ADDRESS,\n `<span class=\"hover-group group-v4 group-6\">${segments\n .slice(0, 2)\n .join('.')}</span>.<span class=\"hover-group group-v4 group-7\">${segments\n .slice(2, 4)\n .join('.')}</span>`,\n );\n }\n}\n", "export const BITS = 128;\nexport const GROUPS = 8;\n\n/**\n * Represents IPv6 address scopes\n * @memberof Address6\n * @static\n */\nexport const SCOPES: { [key: number]: string | undefined } = {\n 0: 'Reserved',\n 1: 'Interface local',\n 2: 'Link local',\n 4: 'Admin local',\n 5: 'Site local',\n 8: 'Organization local',\n 14: 'Global',\n 15: 'Reserved',\n} as const;\n\n/**\n * Represents IPv6 address types\n * @memberof Address6\n * @static\n */\nexport const TYPES: { [key: string]: string | undefined } = {\n 'ff01::1/128': 'Multicast (All nodes on this interface)',\n 'ff01::2/128': 'Multicast (All routers on this interface)',\n 'ff02::1/128': 'Multicast (All nodes on this link)',\n 'ff02::2/128': 'Multicast (All routers on this link)',\n 'ff05::2/128': 'Multicast (All routers in this site)',\n 'ff02::5/128': 'Multicast (OSPFv3 AllSPF routers)',\n 'ff02::6/128': 'Multicast (OSPFv3 AllDR routers)',\n 'ff02::9/128': 'Multicast (RIP routers)',\n 'ff02::a/128': 'Multicast (EIGRP routers)',\n 'ff02::d/128': 'Multicast (PIM routers)',\n 'ff02::16/128': 'Multicast (MLDv2 reports)',\n 'ff01::fb/128': 'Multicast (mDNSv6)',\n 'ff02::fb/128': 'Multicast (mDNSv6)',\n 'ff05::fb/128': 'Multicast (mDNSv6)',\n 'ff02::1:2/128': 'Multicast (All DHCP servers and relay agents on this link)',\n 'ff05::1:2/128': 'Multicast (All DHCP servers and relay agents in this site)',\n 'ff02::1:3/128': 'Multicast (All DHCP servers on this link)',\n 'ff05::1:3/128': 'Multicast (All DHCP servers in this site)',\n '::/128': 'Unspecified',\n '::1/128': 'Loopback',\n 'ff00::/8': 'Multicast',\n 'fe80::/10': 'Link-local unicast',\n} as const;\n\n/**\n * A regular expression that matches bad characters in an IPv6 address\n * @memberof Address6\n * @static\n */\nexport const RE_BAD_CHARACTERS = /([^0-9a-f:/%])/gi;\n\n/**\n * A regular expression that matches an incorrect IPv6 address\n * @memberof Address6\n * @static\n */\nexport const RE_BAD_ADDRESS = /([0-9a-f]{5,}|:{3,}|[^:]:$|^:[^:]|\\/$)/gi;\n\n/**\n * A regular expression that matches an IPv6 subnet\n * @memberof Address6\n * @static\n */\nexport const RE_SUBNET_STRING = /\\/\\d{1,3}(?=%|$)/;\n\n/**\n * A regular expression that matches an IPv6 zone\n * @memberof Address6\n * @static\n */\nexport const RE_ZONE_STRING = /%.*$/;\n\nexport const RE_URL = /^\\[{0,1}([0-9a-f:]+)\\]{0,1}/;\nexport const RE_URL_WITH_PORT = /\\[([0-9a-f:]+)\\]:([0-9]{1,5})/;\n", "/**\n * @returns {String} the string with all zeroes contained in a <span>\n */\nexport function spanAllZeroes(s: string): string {\n return s.replace(/(0+)/g, '<span class=\"zero\">$1</span>');\n}\n\n/**\n * @returns {String} the string with each character contained in a <span>\n */\nexport function spanAll(s: string, offset: number = 0): string {\n const letters = s.split('');\n\n return letters\n .map(\n (n, i) => `<span class=\"digit value-${n} position-${i + offset}\">${spanAllZeroes(n)}</span>`,\n )\n .join('');\n}\n\nfunction spanLeadingZeroesSimple(group: string): string {\n return group.replace(/^(0+)/, '<span class=\"zero\">$1</span>');\n}\n\n/**\n * @returns {String} the string with leading zeroes contained in a <span>\n */\nexport function spanLeadingZeroes(address: string): string {\n const groups = address.split(':');\n\n return groups.map((g) => spanLeadingZeroesSimple(g)).join(':');\n}\n\n/**\n * Groups an address\n * @returns {String} a grouped address\n */\nexport function simpleGroup(addressString: string, offset: number = 0): string[] {\n const groups = addressString.split(':');\n\n return groups.map((g, i) => {\n if (/group-v4/.test(g)) {\n return g;\n }\n\n return `<span class=\"hover-group group-${i + offset}\">${spanLeadingZeroesSimple(g)}</span>`;\n });\n}\n", "import * as v6 from './constants';\n\nexport function groupPossibilities(possibilities: string[]): string {\n return `(${possibilities.join('|')})`;\n}\n\nexport function padGroup(group: string): string {\n if (group.length < 4) {\n return `0{0,${4 - group.length}}${group}`;\n }\n\n return group;\n}\n\nexport const ADDRESS_BOUNDARY = '[^A-Fa-f0-9:]';\n\nexport function simpleRegularExpression(groups: string[]) {\n const zeroIndexes: number[] = [];\n\n groups.forEach((group, i) => {\n const groupInteger = parseInt(group, 16);\n\n if (groupInteger === 0) {\n zeroIndexes.push(i);\n }\n });\n\n // You can technically elide a single 0, this creates the regular expressions\n // to match that eventuality\n const possibilities = zeroIndexes.map((zeroIndex) =>\n groups\n .map((group, i) => {\n if (i === zeroIndex) {\n const elision = i === 0 || i === v6.GROUPS - 1 ? ':' : '';\n\n return groupPossibilities([padGroup(group), elision]);\n }\n\n return padGroup(group);\n })\n .join(':'),\n );\n\n // The simplest case\n possibilities.push(groups.map(padGroup).join(':'));\n\n return groupPossibilities(possibilities);\n}\n\nexport function possibleElisions(\n elidedGroups: number,\n moreLeft?: boolean,\n moreRight?: boolean,\n): string {\n const left = moreLeft ? '' : ':';\n const right = moreRight ? '' : ':';\n\n const possibilities = [];\n\n // 1. elision of everything (::)\n if (!moreLeft && !moreRight) {\n possibilities.push('::');\n }\n\n // 2. complete elision of the middle\n if (moreLeft && moreRight) {\n possibilities.push('');\n }\n\n if ((moreRight && !moreLeft) || (!moreRight && moreLeft)) {\n // 3. complete elision of one side\n possibilities.push(':');\n }\n\n // 4. elision from the left side\n possibilities.push(`${left}(:0{1,4}){1,${elidedGroups - 1}}`);\n\n // 5. elision from the right side\n possibilities.push(`(0{1,4}:){1,${elidedGroups - 1}}${right}`);\n\n // 6. no elision\n possibilities.push(`(0{1,4}:){${elidedGroups - 1}}0{1,4}`);\n\n // 7. elision (including sloppy elision) from the middle\n for (let groups = 1; groups < elidedGroups - 1; groups++) {\n for (let position = 1; position < elidedGroups - groups; position++) {\n possibilities.push(\n `(0{1,4}:){${position}}:(0{1,4}:){${elidedGroups - position - groups - 1}}0{1,4}`,\n );\n }\n }\n\n return groupPossibilities(possibilities);\n}\n", "/* eslint-disable prefer-destructuring */\n/* eslint-disable no-param-reassign */\n\nimport * as common from './common';\nimport * as constants4 from './v4/constants';\nimport * as constants6 from './v6/constants';\nimport * as helpers from './v6/helpers';\nimport { Address4 } from './ipv4';\nimport {\n ADDRESS_BOUNDARY,\n possibleElisions,\n simpleRegularExpression,\n} from './v6/regular-expressions';\nimport { AddressError } from './address-error';\nimport { testBit } from './common';\n\nfunction assert(condition: any): asserts condition {\n if (!condition) {\n throw new Error('Assertion failed.');\n }\n}\n\nfunction addCommas(number: string): string {\n const r = /(\\d+)(\\d{3})/;\n\n while (r.test(number)) {\n number = number.replace(r, '$1,$2');\n }\n\n return number;\n}\n\nfunction spanLeadingZeroes4(n: string): string {\n n = n.replace(/^(0{1,})([1-9]+)$/, '<span class=\"parse-error\">$1</span>$2');\n n = n.replace(/^(0{1,})(0)$/, '<span class=\"parse-error\">$1</span>$2');\n\n return n;\n}\n\n/*\n * A helper function to compact an array\n */\nfunction compact(address: string[], slice: number[]) {\n const s1 = [];\n const s2 = [];\n let i;\n\n for (i = 0; i < address.length; i++) {\n if (i < slice[0]) {\n s1.push(address[i]);\n } else if (i > slice[1]) {\n s2.push(address[i]);\n }\n }\n\n return s1.concat(['compact']).concat(s2);\n}\n\nfunction paddedHex(octet: string): string {\n return parseInt(octet, 16).toString(16).padStart(4, '0');\n}\n\nfunction unsignByte(b: number) {\n // eslint-disable-next-line no-bitwise\n return b & 0xff;\n}\n\ninterface SixToFourProperties {\n prefix: string;\n gateway: string;\n}\n\ninterface TeredoProperties {\n prefix: string;\n server4: string;\n client4: string;\n flags: string;\n coneNat: boolean;\n microsoft: {\n reserved: boolean;\n universalLocal: boolean;\n groupIndividual: boolean;\n nonce: string;\n };\n udpPort: string;\n}\n\n/**\n * Represents an IPv6 address\n * @class Address6\n * @param {string} address - An IPv6 address string\n * @param {number} [groups=8] - How many octets to parse\n * @example\n * var address = new Address6('2001::/32');\n */\nexport class Address6 {\n address4?: Address4;\n address: string;\n addressMinusSuffix: string = '';\n elidedGroups?: number;\n elisionBegin?: number;\n elisionEnd?: number;\n groups: number;\n parsedAddress4?: string;\n parsedAddress: string[];\n parsedSubnet: string = '';\n subnet: string = '/128';\n subnetMask: number = 128;\n v4: boolean = false;\n zone: string = '';\n\n constructor(address: string, optionalGroups?: number) {\n if (optionalGroups === undefined) {\n this.groups = constants6.GROUPS;\n } else {\n this.groups = optionalGroups;\n }\n\n this.address = address;\n\n const subnet = constants6.RE_SUBNET_STRING.exec(address);\n\n if (subnet) {\n this.parsedSubnet = subnet[0].replace('/', '');\n this.subnetMask = parseInt(this.parsedSubnet, 10);\n this.subnet = `/${this.subnetMask}`;\n\n if (\n Number.isNaN(this.subnetMask) ||\n this.subnetMask < 0 ||\n this.subnetMask > constants6.BITS\n ) {\n throw new AddressError('Invalid subnet mask.');\n }\n\n address = address.replace(constants6.RE_SUBNET_STRING, '');\n } else if (/\\//.test(address)) {\n throw new AddressError('Invalid subnet mask.');\n }\n\n const zone = constants6.RE_ZONE_STRING.exec(address);\n\n if (zone) {\n this.zone = zone[0];\n\n address = address.replace(constants6.RE_ZONE_STRING, '');\n }\n\n this.addressMinusSuffix = address;\n\n this.parsedAddress = this.parse(this.addressMinusSuffix);\n }\n\n static isValid(address: string): boolean {\n try {\n // eslint-disable-next-line no-new\n new Address6(address);\n\n return true;\n } catch (e) {\n return false;\n }\n }\n\n /**\n * Convert a BigInt to a v6 address object\n * @memberof Address6\n * @static\n * @param {bigint} bigInt - a BigInt to convert\n * @returns {Address6}\n * @example\n * var bigInt = BigInt('1000000000000');\n * var address = Address6.fromBigInt(bigInt);\n * address.correctForm(); // '::e8:d4a5:1000'\n */\n static fromBigInt(bigInt: bigint): Address6 {\n const hex = bigInt.toString(16).padStart(32, '0');\n const groups = [];\n let i;\n\n for (i = 0; i < constants6.GROUPS; i++) {\n groups.push(hex.slice(i * 4, (i + 1) * 4));\n }\n\n return new Address6(groups.join(':'));\n }\n\n /**\n * Convert a URL (with optional port number) to an address object\n * @memberof Address6\n * @static\n * @param {string} url - a URL with optional port number\n * @example\n * var addressAndPort = Address6.fromURL('http://[ffff::]:8080/foo/');\n * addressAndPort.address.correctForm(); // 'ffff::'\n * addressAndPort.port; // 8080\n */\n static fromURL(url: string) {\n let host: string;\n let port: string | number | null = null;\n let result: string[] | null;\n\n // If we have brackets parse them and find a port\n if (url.indexOf('[') !== -1 && url.indexOf(']:') !== -1) {\n result = constants6.RE_URL_WITH_PORT.exec(url);\n\n if (result === null) {\n return {\n error: 'failed to parse address with port',\n address: null,\n port: null,\n };\n }\n\n host = result[1];\n port = result[2];\n // If there's a URL extract the address\n } else if (url.indexOf('/') !== -1) {\n // Remove the protocol prefix\n url = url.replace(/^[a-z0-9]+:\\/\\//, '');\n\n // Parse the address\n result = constants6.RE_URL.exec(url);\n\n if (result === null) {\n return {\n error: 'failed to parse address from URL',\n address: null,\n port: null,\n };\n }\n\n host = result[1];\n // Otherwise just assign the URL to the host and let the library parse it\n } else {\n host = url;\n }\n\n // If there's a port convert it to an integer\n if (port) {\n port = parseInt(port, 10);\n\n // squelch out of range ports\n if (port < 0 || port > 65536) {\n port = null;\n }\n } else {\n // Standardize `undefined` to `null`\n port = null;\n }\n\n return {\n address: new Address6(host),\n port,\n };\n }\n\n /**\n * Create an IPv6-mapped address given an IPv4 address\n * @memberof Address6\n * @static\n * @param {string} address - An IPv4 address string\n * @returns {Address6}\n * @example\n * var address = Address6.fromAddress4('192.168.0.1');\n * address.correctForm(); // '::ffff:c0a8:1'\n * address.to4in6(); // '::ffff:192.168.0.1'\n */\n static fromAddress4(address: string): Address6 {\n const address4 = new Address4(address);\n\n const mask6 = constants6.BITS - (constants4.BITS - address4.subnetMask);\n\n return new Address6(`::ffff:${address4.correctForm()}/${mask6}`);\n }\n\n /**\n * Return an address from ip6.arpa form\n * @memberof Address6\n * @static\n * @param {string} arpaFormAddress - an 'ip6.arpa' form address\n * @returns {Adress6}\n * @example\n * var address = Address6.fromArpa(e.f.f.f.3.c.2.6.f.f.f.e.6.6.8.e.1.0.6.7.9.4.e.c.0.0.0.0.1.0.0.2.ip6.arpa.)\n * address.correctForm(); // '2001:0:ce49:7601:e866:efff:62c3:fffe'\n */\n static fromArpa(arpaFormAddress: string): Address6 {\n // remove ending \".ip6.arpa.\" or just \".\"\n let address = arpaFormAddress.replace(/(\\.ip6\\.arpa)?\\.$/, '');\n const semicolonAmount = 7;\n\n // correct ip6.arpa form with ending removed will be 63 characters\n if (address.length !== 63) {\n throw new AddressError(\"Invalid 'ip6.arpa' form.\");\n }\n\n const parts = address.split('.').reverse();\n\n for (let i = semicolonAmount; i > 0; i--) {\n const insertIndex = i * 4;\n parts.splice(insertIndex, 0, ':');\n }\n\n address = parts.join('');\n\n return new Address6(address);\n }\n\n /**\n * Return the Microsoft UNC transcription of the address\n * @memberof Address6\n * @instance\n * @returns {String} the Microsoft UNC transcription of the address\n */\n microsoftTranscription(): string {\n return `${this.correctForm().replace(/:/g, '-')}.ipv6-literal.net`;\n }\n\n /**\n * Return the first n bits of the address, defaulting to the subnet mask\n * @memberof Address6\n * @instance\n * @param {number} [mask=subnet] - the number of bits to mask\n * @returns {String} the first n bits of the address as a string\n */\n mask(mask: number = this.subnetMask): string {\n return this.getBitsBase2(0, mask);\n }\n\n /**\n * Return the number of possible subnets of a given size in the address\n * @memberof Address6\n * @instance\n * @param {number} [subnetSize=128] - the subnet size\n * @returns {String}\n */\n // TODO: probably useful to have a numeric version of this too\n possibleSubnets(subnetSize: number = 128): string {\n const availableBits = constants6.BITS - this.subnetMask;\n const subnetBits = Math.abs(subnetSize - constants6.BITS);\n const subnetPowers = availableBits - subnetBits;\n\n if (subnetPowers < 0) {\n return '0';\n }\n\n return addCommas((BigInt('2') ** BigInt(subnetPowers)).toString(10));\n }\n\n /**\n * Helper function getting start address.\n * @memberof Address6\n * @instance\n * @returns {bigint}\n */\n _startAddress(): bigint {\n return BigInt(`0b${this.mask() + '0'.repeat(constants6.BITS - this.subnetMask)}`);\n }\n\n /**\n * The first address in the range given by this address' subnet\n * Often referred to as the Network Address.\n * @memberof Address6\n * @instance\n * @returns {Address6}\n */\n startAddress(): Address6 {\n return Address6.fromBigInt(this._startAddress());\n }\n\n /**\n * The first host address in the range given by this address's subnet ie\n * the first address after the Network Address\n * @memberof Address6\n * @instance\n * @returns {Address6}\n */\n startAddressExclusive(): Address6 {\n const adjust = BigInt('1');\n return Address6.fromBigInt(this._startAddress() + adjust);\n }\n\n /**\n * Helper function getting end address.\n * @memberof Address6\n * @instance\n * @returns {bigint}\n */\n _endAddress(): bigint {\n return BigInt(`0b${this.mask() + '1'.repeat(constants6.BITS - this.subnetMask)}`);\n }\n\n /**\n * The last address in the range given by this address' subnet\n * Often referred to as the Broadcast\n * @memberof Address6\n * @instance\n * @returns {Address6}\n */\n endAddress(): Address6 {\n return Address6.fromBigInt(this._endAddress());\n }\n\n /**\n * The last host address in the range given by this address's subnet ie\n * the last address prior to the Broadcast Address\n * @memberof Address6\n * @instance\n * @returns {Address6}\n */\n endAddressExclusive(): Address6 {\n const adjust = BigInt('1');\n return Address6.fromBigInt(this._endAddress() - adjust);\n }\n\n /**\n * Return the scope of the address\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n getScope(): string {\n let scope = constants6.SCOPES[parseInt(this.getBits(12, 16).toString(10), 10)];\n\n if (this.getType() === 'Global unicast' && scope !== 'Link local') {\n scope = 'Global';\n }\n\n return scope || 'Unknown';\n }\n\n /**\n * Return the type of the address\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n getType(): string {\n for (const subnet of Object.keys(constants6.TYPES)) {\n if (this.isInSubnet(new Address6(subnet))) {\n return constants6.TYPES[subnet] as string;\n }\n }\n\n return 'Global unicast';\n }\n\n /**\n * Return the bits in the given range as a BigInt\n * @memberof Address6\n * @instance\n * @returns {bigint}\n */\n getBits(start: number, end: number): bigint {\n return BigInt(`0b${this.getBitsBase2(start, end)}`);\n }\n\n /**\n * Return the bits in the given range as a base-2 string\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n getBitsBase2(start: number, end: number): string {\n return this.binaryZeroPad().slice(start, end);\n }\n\n /**\n * Return the bits in the given range as a base-16 string\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n getBitsBase16(start: number, end: number): string {\n const length = end - start;\n\n if (length % 4 !== 0) {\n throw new Error('Length of bits to retrieve must be divisible by four');\n }\n\n return this.getBits(start, end)\n .toString(16)\n .padStart(length / 4, '0');\n }\n\n /**\n * Return the bits that are set past the subnet mask length\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n getBitsPastSubnet(): string {\n return this.getBitsBase2(this.subnetMask, constants6.BITS);\n }\n\n /**\n * Return the reversed ip6.arpa form of the address\n * @memberof Address6\n * @param {Object} options\n * @param {boolean} options.omitSuffix - omit the \"ip6.arpa\" suffix\n * @instance\n * @returns {String}\n */\n reverseForm(options?: common.ReverseFormOptions): string {\n if (!options) {\n options = {};\n }\n\n const characters = Math.floor(this.subnetMask / 4);\n\n const reversed = this.canonicalForm()\n .replace(/:/g, '')\n .split('')\n .slice(0, characters)\n .reverse()\n .join('.');\n\n if (characters > 0) {\n if (options.omitSuffix) {\n return reversed;\n }\n\n return `${reversed}.ip6.arpa.`;\n }\n\n if (options.omitSuffix) {\n return '';\n }\n\n return 'ip6.arpa.';\n }\n\n /**\n * Return the correct form of the address\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n correctForm(): string {\n let i;\n let groups = [];\n\n let zeroCounter = 0;\n const zeroes = [];\n\n for (i = 0; i < this.parsedAddress.length; i++) {\n const value = parseInt(this.parsedAddress[i], 16);\n\n if (value === 0) {\n zeroCounter++;\n }\n\n if (value !== 0 && zeroCounter > 0) {\n if (zeroCounter > 1) {\n zeroes.push([i - zeroCounter, i - 1]);\n }\n\n zeroCounter = 0;\n }\n }\n\n // Do we end with a string of zeroes?\n if (zeroCounter > 1) {\n zeroes.push([this.parsedAddress.length - zeroCounter, this.parsedAddress.length - 1]);\n }\n\n const zeroLengths = zeroes.map((n) => n[1] - n[0] + 1);\n\n if (zeroes.length > 0) {\n const index = zeroLengths.indexOf(Math.max(...zeroLengths) as number);\n\n groups = compact(this.parsedAddress, zeroes[index]);\n } else {\n groups = this.parsedAddress;\n }\n\n for (i = 0; i < groups.length; i++) {\n if (groups[i] !== 'compact') {\n groups[i] = parseInt(groups[i], 16).toString(16);\n }\n }\n\n let correct = groups.join(':');\n\n correct = correct.replace(/^compact$/, '::');\n correct = correct.replace(/(^compact)|(compact$)/, ':');\n correct = correct.replace(/compact/, '');\n\n return correct;\n }\n\n /**\n * Return a zero-padded base-2 string representation of the address\n * @memberof Address6\n * @instance\n * @returns {String}\n * @example\n * var address = new Address6('2001:4860:4001:803::1011');\n * address.binaryZeroPad();\n * // '0010000000000001010010000110000001000000000000010000100000000011\n * // 0000000000000000000000000000000000000000000000000001000000010001'\n */\n binaryZeroPad(): string {\n return this.bigInt().toString(2).padStart(constants6.BITS, '0');\n }\n\n // TODO: Improve the semantics of this helper function\n parse4in6(address: string): string {\n const groups = address.split(':');\n const lastGroup = groups.slice(-1)[0];\n\n const address4 = lastGroup.match(constants4.RE_ADDRESS);\n\n if (address4) {\n this.parsedAddress4 = address4[0];\n this.address4 = new Address4(this.parsedAddress4);\n\n for (let i = 0; i < this.address4.groups; i++) {\n if (/^0[0-9]+/.test(this.address4.parsedAddress[i])) {\n throw new AddressError(\n \"IPv4 addresses can't have leading zeroes.\",\n address.replace(\n constants4.RE_ADDRESS,\n this.address4.parsedAddress.map(spanLeadingZeroes4).join('.'),\n ),\n );\n }\n }\n\n this.v4 = true;\n\n groups[groups.length - 1] = this.address4.toGroup6();\n\n address = groups.join(':');\n }\n\n return address;\n }\n\n // TODO: Make private?\n parse(address: string): string[] {\n address = this.parse4in6(address);\n\n const badCharacters = address.match(constants6.RE_BAD_CHARACTERS);\n\n if (badCharacters) {\n throw new AddressError(\n `Bad character${\n badCharacters.length > 1 ? 's' : ''\n } detected in address: ${badCharacters.join('')}`,\n address.replace(constants6.RE_BAD_CHARACTERS, '<span class=\"parse-error\">$1</span>'),\n );\n }\n\n const badAddress = address.match(constants6.RE_BAD_ADDRESS);\n\n if (badAddress) {\n throw new AddressError(\n `Address failed regex: ${badAddress.join('')}`,\n address.replace(constants6.RE_BAD_ADDRESS, '<span class=\"parse-error\">$1</span>'),\n );\n }\n\n let groups: string[] = [];\n\n const halves = address.split('::');\n\n if (halves.length === 2) {\n let first = halves[0].split(':');\n let last = halves[1].split(':');\n\n if (first.length === 1 && first[0] === '') {\n first = [];\n }\n\n if (last.length === 1 && last[0] === '') {\n last = [];\n }\n\n const remaining = this.groups - (first.length + last.length);\n\n if (!remaining) {\n throw new AddressError('Error parsing groups');\n }\n\n this.elidedGroups = remaining;\n\n this.elisionBegin = first.length;\n this.elisionEnd = first.length + this.elidedGroups;\n\n groups = groups.concat(first);\n\n for (let i = 0; i < remaining; i++) {\n groups.push('0');\n }\n\n groups = groups.concat(last);\n } else if (halves.length === 1) {\n groups = address.split(':');\n\n this.elidedGroups = 0;\n } else {\n throw new AddressError('Too many :: groups found');\n }\n\n groups = groups.map((group: string) => parseInt(group, 16).toString(16));\n\n if (groups.length !== this.groups) {\n throw new AddressError('Incorrect number of groups found');\n }\n\n return groups;\n }\n\n /**\n * Return the canonical form of the address\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n canonicalForm(): string {\n return this.parsedAddress.map(paddedHex).join(':');\n }\n\n /**\n * Return the decimal form of the address\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n decimal(): string {\n return this.parsedAddress.map((n) => parseInt(n, 16).toString(10).padStart(5, '0')).join(':');\n }\n\n /**\n * Return the address as a BigInt\n * @memberof Address6\n * @instance\n * @returns {bigint}\n */\n bigInt(): bigint {\n return BigInt(`0x${this.parsedAddress.map(paddedHex).join('')}`);\n }\n\n /**\n * Return the last two groups of this address as an IPv4 address string\n * @memberof Address6\n * @instance\n * @returns {Address4}\n * @example\n * var address = new Address6('2001:4860:4001::1825:bf11');\n * address.to4().correctForm(); // '24.37.191.17'\n */\n to4(): Address4 {\n const binary = this.binaryZeroPad().split('');\n\n return Address4.fromHex(BigInt(`0b${binary.slice(96, 128).join('')}`).toString(16));\n }\n\n /**\n * Return the v4-in-v6 form of the address\n * @memberof Address6\n * @instance\n * @returns {String}\n */\n to4in6(): string {\n const address4 = this.to4();\n const address6 = new Address6(this.parsedAddress.slice(0, 6).join(':'), 6);\n\n const correct = address6.correctForm();\n\n let infix = '';\n\n if (!/:$/.test(correct)) {\n infix = ':';\n }\n\n return correct + infix + address4.address;\n }\n\n /**\n * Return an object containing the Teredo properties of the address\n * @memberof Address6\n * @instance\n * @returns {Object}\n */\n inspectTeredo(): TeredoProperties {\n /*\n - Bits 0 to 31 are set to the Teredo prefix (normally 2001:0000::/32).\n - Bits 32 to 63 embed the primary IPv4 address of the Teredo server that\n is used.\n - Bits 64 to 79 can be used to define some flags. Currently only the\n higher order bit is used; it is set to 1 if the Teredo client is\n located behind a cone NAT, 0 otherwise. For Microsoft's Windows Vista\n and Windows Server 2008 implementations, more bits are used. In those\n implementations, the format for these 16 bits is \"CRAAAAUG AAAAAAAA\",\n where \"C\" remains the \"Cone\" flag. The \"R\" bit is reserved for future\n use. The \"U\" bit is for the Universal/Local flag (set to 0). The \"G\" bit\n is Individual/Group flag (set to 0). The A bits are set to a 12-bit\n randomly generated number chosen by the Teredo client to introduce\n additional protection for the Teredo node against IPv6-based scanning\n attacks.\n - Bits 80 to 95 contains the obfuscated UDP port number. This is the\n port number that is mapped by the NAT to the Teredo client with all\n bits inverted.\n - Bits 96 to 127 contains the obfuscated IPv4 address. This is the\n public IPv4 address of the NAT with all bits inverted.\n */\n const prefix = this.getBitsBase16(0, 32);\n\n const bitsForUdpPort: bigint = this.getBits(80, 96);\n // eslint-disable-next-line no-bitwise\n const udpPort = (bitsForUdpPort ^ BigInt('0xffff')).toString();\n\n const server4 = Address4.fromHex(this.getBitsBase16(32, 64));\n\n const bitsForClient4 = this.getBits(96, 128);\n // eslint-disable-next-line no-bitwise\n const client4 = Address4.fromHex((bitsForClient4 ^ BigInt('0xffffffff')).toString(16));\n\n const flagsBase2 = this.getBitsBase2(64, 80);\n\n const coneNat = testBit(flagsBase2, 15);\n const reserved = testBit(flagsBase2, 14);\n const groupIndividual = testBit(flagsBase2, 8);\n const universalLocal = testBit(flagsBase2, 9);\n const nonce = BigInt(`0b${flagsBase2.slice(2, 6) + flagsBase2.slice(8, 16)}`).toString(10);\n\n return {\n prefix: `${prefix.slice(0, 4)}:${prefix.slice(4, 8)}`,\n server4: server4.address,\n client4: client4.address,\n flags: flagsBase2,\n coneNat,\n microsoft: {\n reserved,\n universalLocal,\n groupIndividual,\n nonce,\n },\n udpPort,\n };\n }\n\n /**\n * Return an object containing the 6to4 properties of the address\n * @memberof Address6\n * @instance\n * @returns {Object}\n */\n inspect6to4(): SixToFourProperties {\n /*\n - Bits 0 to 15 are set to the 6to4 prefix (2002::/16).\n - Bits 16 to 48 embed the IPv4 address of the 6to4 gateway that is used.\n */\n\n const prefix = this.getBitsBase16(0, 16);\n\n const gateway = Address4.fromHex(this.getBitsBase16(16, 48));\n\n return {\n prefix: prefix.slice(0, 4),\n gateway: gateway.address,\n };\n }\n\n /**\n * Return a v6 6to4 address from a v6 v4inv6 address\n * @memberof Address6\n * @instance\n * @returns {Address6}\n */\n to6to4(): Address6 | null {\n if (!this.is4()) {\n return null;\n }\n\n const addr6to4 = [\n '2002',\n this.getBitsBase16(96, 112),\n this.getBitsBase16(112, 128),\n '',\n '/16',\n ].join(':');\n\n return new Address6(addr6to4);\n }\n\n /**\n * Return a byte array\n * @memberof Address6\n * @instance\n * @returns {Array}\n */\n toByteArray(): number[] {\n const valueWithoutPadding = this.bigInt().toString(16);\n const leadingPad = '0'.repeat(valueWithoutPadding.length % 2);\n\n const value = `${leadingPad}${valueWithoutPadding}`;\n\n const bytes = [];\n for (let i = 0, length = value.length; i < length; i += 2) {\n bytes.push(parseInt(value.substring(i, i + 2), 16));\n }\n\n return bytes;\n }\n\n /**\n * Return an unsigned byte array\n * @memberof Address6\n * @instance\n * @returns {Array}\n */\n toUnsignedByteArray(): number[] {\n return this.toByteArray().map(unsignByte);\n }\n\n /**\n * Convert a byte array to an Address6 object\n * @memberof Address6\n * @static\n * @returns {Address6}\n */\n static fromByteArray(bytes: Array<any>): Address6 {\n return this.fromUnsignedByteArray(bytes.map(unsignByte));\n }\n\n /**\n * Convert an unsigned byte array to an Address6 object\n * @memberof Address6\n * @static\n * @returns {Address6}\n */\n static fromUnsignedByteArray(bytes: Array<any>): Address6 {\n const BYTE_MAX = BigInt('256');\n let result = BigInt('0');\n let multiplier = BigInt('1');\n\n for (let i = bytes.length - 1; i >= 0; i--) {\n result += multiplier * BigInt(bytes[i].toString(10));\n\n multiplier *= BYTE_MAX;\n }\n\n return Address6.fromBigInt(result);\n }\n\n // #region Attributes\n /**\n * Returns true if the given address is in the subnet of the current address\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n isInSubnet = common.isInSubnet;\n\n /**\n * Returns true if the address is correct, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n isCorrect = common.isCorrect(constants6.BITS);\n\n /**\n * Returns true if the address is in the canonical form, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n isCanonical(): boolean {\n return this.addressMinusSuffix === this.canonicalForm();\n }\n\n /**\n * Returns true if the address is a link local address, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n isLinkLocal(): boolean {\n // Zeroes are required, i.e. we can't check isInSubnet with 'fe80::/10'\n if (\n this.getBitsBase2(0, 64) ===\n '1111111010000000000000000000000000000000000000000000000000000000'\n ) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Returns true if the address is a multicast address, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n isMulticast(): boolean {\n return this.getType() === 'Multicast';\n }\n\n /**\n * Returns true if the address is a v4-in-v6 address, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n is4(): boolean {\n return this.v4;\n }\n\n /**\n * Returns true if the address is a Teredo address, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n isTeredo(): boolean {\n return this.isInSubnet(new Address6('2001::/32'));\n }\n\n /**\n * Returns true if the address is a 6to4 address, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n is6to4(): boolean {\n return this.isInSubnet(new Address6('2002::/16'));\n }\n\n /**\n * Returns true if the address is a loopback address, false otherwise\n * @memberof Address6\n * @instance\n * @returns {boolean}\n */\n isLoopback(): boolean {\n return this.getType() === 'Loopback';\n }\n // #endregion\n\n // #region HTML\n /**\n * @returns {String} the address in link form with a default port of 80\n */\n href(optionalPort?: number | string): string {\n if (optionalPort === undefined) {\n optionalPort = '';\n } else {\n optionalPort = `:${optionalPort}`;\n }\n\n return `http://[${this.correctForm()}]${optionalPort}/`;\n }\n\n /**\n * @returns {String} a link suitable for conveying the address via a URL hash\n */\n link(options?: { className?: string; prefix?: string; v4?: boolean }): string {\n if (!options) {\n options = {};\n }\n\n if (options.className === undefined) {\n options.className = '';\n }\n\n if (options.prefix === undefined) {\n options.prefix = '/#address=';\n }\n\n if (options.v4 === undefined) {\n options.v4 = false;\n }\n\n let formFunction = this.correctForm;\n\n if (options.v4) {\n formFunction = this.to4in6;\n }\n\n const form = formFunction.call(this);\n\n if (options.className) {\n return `<a href=\"${options.prefix}${form}\" class=\"${options.className}\">${form}</a>`;\n }\n\n return `<a href=\"${options.prefix}${form}\">${form}</a>`;\n }\n\n /**\n * Groups an address\n * @returns {String}\n */\n group(): string {\n if (this.elidedGroups === 0) {\n // The simple case\n return helpers.simpleGroup(this.address).join(':');\n }\n\n assert(typeof this.elidedGroups === 'number');\n assert(typeof this.elisionBegin === 'number');\n\n // The elided case\n const output = [];\n\n const [left, right] = this.address.split('::');\n\n if (left.length) {\n output.push(...helpers.simpleGroup(left));\n } else {\n output.push('');\n }\n\n const classes = ['hover-group'];\n\n for (let i = this.elisionBegin; i < this.elisionBegin + this.elidedGroups; i++) {\n classes.push(`group-${i}`);\n }\n\n output.push(`<span class=\"${classes.join(' ')}\"></span>`);\n\n if (right.length) {\n output.push(...helpers.simpleGroup(right, this.elisionEnd));\n } else {\n output.push('');\n }\n\n if (this.is4()) {\n assert(this.address4 instanceof Address4);\n\n output.pop();\n output.push(this.address4.groupForV6());\n }\n\n return output.join(':');\n }\n // #endregion\n\n // #region Regular expressions\n /**\n * Generate a regular expression string that can be used to find or validate\n * all variations of this address\n * @memberof Address6\n * @instance\n * @param {boolean} substringSearch\n * @returns {string}\n */\n regularExpressionString(this: Address6, substringSearch: boolean = false): string {\n let output: string[] = [];\n\n // TODO: revisit why this is necessary\n const address6 = new Address6(this.correctForm());\n\n if (address6.elidedGroups === 0) {\n // The simple case\n output.push(simpleRegularExpression(address6.parsedAddress));\n } else if (address6.elidedGroups === constants6.GROUPS) {\n // A completely elided address\n output.push(possibleElisions(constants6.GROUPS));\n } else {\n // A partially elided address\n const halves = address6.address.split('::');\n\n if (halves[0].length) {\n output.push(simpleRegularExpression(halves[0].split(':')));\n }\n\n assert(typeof address6.elidedGroups === 'number');\n\n output.push(\n possibleElisions(address6.elidedGroups, halves[0].length !== 0, halves[1].length !== 0),\n );\n\n if (halves[1].length) {\n output.push(simpleRegularExpression(halves[1].split(':')));\n }\n\n output = [output.join(':')];\n }\n\n if (!substringSearch) {\n output = [\n '(?=^|',\n ADDRESS_BOUNDARY,\n '|[^\\\\w\\\\:])(',\n ...output,\n ')(?=[^\\\\w\\\\:]|',\n ADDRESS_BOUNDARY,\n '|$)',\n ];\n }\n\n return output.join('');\n }\n\n /**\n * Generate a regular expression that can be used to find or validate all\n * variations of this address.\n * @memberof Address6\n * @instance\n * @param {boolean} substringSearch\n * @returns {RegExp}\n */\n regularExpression(this: Address6, substringSearch: boolean = false): RegExp {\n return new RegExp(this.regularExpressionString(substringSearch), 'i');\n }\n // #endregion\n}\n", "export { Address4 } from './ipv4';\nexport { Address6 } from './ipv6';\nexport { AddressError } from './address-error';\n\nimport * as helpers from './v6/helpers';\n\nexport const v6 = { helpers };\n", "import { Database } from 'bun:sqlite';\nimport { existsSync, statSync } from 'fs';\nimport type { Observation, Summary, SearchFilters, TimelineEntry } from '../../types/worker-types.js';\n\n/**\n * Advanced search module for Kiro Memory\n * Supports FTS5 full-text search with LIKE fallback\n */\n\n/**\n * BM25 weights for FTS5 columns: title, text, narrative, concepts.\n * Higher values = more relevant column in ranking.\n */\nconst BM25_WEIGHTS = '10.0, 1.0, 5.0, 3.0';\n\n/** Escape LIKE wildcard characters to prevent pattern injection */\nfunction escapeLikePattern(input: string): string {\n return input.replace(/[%_\\\\]/g, '\\\\$&');\n}\n\n/**\n * Sanitize a query for FTS5: wraps each term in quotes\n * to prevent reserved operators (AND, OR, NOT, NEAR, *, ^, :)\n * from causing parsing errors. Limits length and term count to prevent ReDoS.\n */\nfunction sanitizeFTS5Query(query: string): string {\n // Limit input length before parsing\n const trimmed = query.length > 10_000 ? query.substring(0, 10_000) : query;\n\n const terms = trimmed\n .replace(/[\"\"\\u0022]/g, '')\n .split(/\\s+/)\n .filter(t => t.length > 0)\n .slice(0, 100)\n .map(t => `\"${t}\"`);\n\n return terms.join(' ');\n}\n\n/**\n * Search observations with FTS5 (full-text) and optional filters.\n * Sanitizes the FTS5 query and falls back to LIKE on error.\n */\nexport function searchObservationsFTS(\n db: Database,\n query: string,\n filters: SearchFilters = {}\n): Observation[] {\n const limit = filters.limit || 50;\n\n try {\n const safeQuery = sanitizeFTS5Query(query);\n if (!safeQuery) return searchObservationsLIKE(db, query, filters);\n\n let sql = `\n SELECT o.* FROM observations o\n JOIN observations_fts fts ON o.id = fts.rowid\n WHERE observations_fts MATCH ?\n `;\n const params: (string | number)[] = [safeQuery];\n\n if (filters.project) {\n sql += ' AND o.project = ?';\n params.push(filters.project);\n }\n if (filters.type) {\n sql += ' AND o.type = ?';\n params.push(filters.type);\n }\n if (filters.dateStart) {\n sql += ' AND o.created_at_epoch >= ?';\n params.push(filters.dateStart);\n }\n if (filters.dateEnd) {\n sql += ' AND o.created_at_epoch <= ?';\n params.push(filters.dateEnd);\n }\n\n sql += ` ORDER BY bm25(observations_fts, ${BM25_WEIGHTS}) LIMIT ?`;\n params.push(limit);\n\n const stmt = db.query(sql);\n return stmt.all(...params) as Observation[];\n } catch {\n // Fallback to LIKE if FTS5 is unavailable or query is malformed\n return searchObservationsLIKE(db, query, filters);\n }\n}\n\n/**\n * FTS5 search that exposes the raw rank for scoring.\n * The FTS5 rank is negative: more negative = more relevant.\n * Uses sanitizeFTS5Query for safety, falls back to LIKE without rank.\n */\nexport function searchObservationsFTSWithRank(\n db: Database,\n query: string,\n filters: SearchFilters = {}\n): Array<Observation & { fts5_rank: number }> {\n const limit = filters.limit || 50;\n\n try {\n const safeQuery = sanitizeFTS5Query(query);\n if (!safeQuery) return [];\n\n let sql = `\n SELECT o.*, bm25(observations_fts, ${BM25_WEIGHTS}) as fts5_rank FROM observations o\n JOIN observations_fts fts ON o.id = fts.rowid\n WHERE observations_fts MATCH ?\n `;\n const params: (string | number)[] = [safeQuery];\n\n if (filters.project) {\n sql += ' AND o.project = ?';\n params.push(filters.project);\n }\n if (filters.type) {\n sql += ' AND o.type = ?';\n params.push(filters.type);\n }\n if (filters.dateStart) {\n sql += ' AND o.created_at_epoch >= ?';\n params.push(filters.dateStart);\n }\n if (filters.dateEnd) {\n sql += ' AND o.created_at_epoch <= ?';\n params.push(filters.dateEnd);\n }\n\n sql += ` ORDER BY bm25(observations_fts, ${BM25_WEIGHTS}) LIMIT ?`;\n params.push(limit);\n\n const stmt = db.query(sql);\n return stmt.all(...params) as Array<Observation & { fts5_rank: number }>;\n } catch {\n // Fallback: no rank available\n return [];\n }\n}\n\n/**\n * Fallback: LIKE search on observations\n */\nexport function searchObservationsLIKE(\n db: Database,\n query: string,\n filters: SearchFilters = {}\n): Observation[] {\n const limit = filters.limit || 50;\n const pattern = `%${escapeLikePattern(query)}%`;\n let sql = `\n SELECT * FROM observations\n WHERE (title LIKE ? ESCAPE '\\\\' OR text LIKE ? ESCAPE '\\\\' OR narrative LIKE ? ESCAPE '\\\\' OR concepts LIKE ? ESCAPE '\\\\')\n `;\n const params: (string | number)[] = [pattern, pattern, pattern, pattern];\n\n if (filters.project) {\n sql += ' AND project = ?';\n params.push(filters.project);\n }\n if (filters.type) {\n sql += ' AND type = ?';\n params.push(filters.type);\n }\n if (filters.dateStart) {\n sql += ' AND created_at_epoch >= ?';\n params.push(filters.dateStart);\n }\n if (filters.dateEnd) {\n sql += ' AND created_at_epoch <= ?';\n params.push(filters.dateEnd);\n }\n\n sql += ' ORDER BY created_at_epoch DESC, id DESC LIMIT ?';\n params.push(limit);\n\n const stmt = db.query(sql);\n return stmt.all(...params) as Observation[];\n}\n\n/**\n * Search summaries with filters\n */\nexport function searchSummariesFiltered(\n db: Database,\n query: string,\n filters: SearchFilters = {}\n): Summary[] {\n const limit = filters.limit || 20;\n const pattern = `%${escapeLikePattern(query)}%`;\n let sql = `\n SELECT * FROM summaries\n WHERE (request LIKE ? ESCAPE '\\\\' OR learned LIKE ? ESCAPE '\\\\' OR completed LIKE ? ESCAPE '\\\\' OR notes LIKE ? ESCAPE '\\\\' OR next_steps LIKE ? ESCAPE '\\\\')\n `;\n const params: (string | number)[] = [pattern, pattern, pattern, pattern, pattern];\n\n if (filters.project) {\n sql += ' AND project = ?';\n params.push(filters.project);\n }\n if (filters.dateStart) {\n sql += ' AND created_at_epoch >= ?';\n params.push(filters.dateStart);\n }\n if (filters.dateEnd) {\n sql += ' AND created_at_epoch <= ?';\n params.push(filters.dateEnd);\n }\n\n sql += ' ORDER BY created_at_epoch DESC, id DESC LIMIT ?';\n params.push(limit);\n\n const stmt = db.query(sql);\n return stmt.all(...params) as Summary[];\n}\n\n/**\n * Retrieve observations by ID (batch)\n */\nexport function getObservationsByIds(db: Database, ids: number[]): Observation[] {\n if (!Array.isArray(ids) || ids.length === 0) return [];\n\n // Validate and filter: only positive integers, max 500 per query\n const validIds = ids\n .filter(id => typeof id === 'number' && Number.isInteger(id) && id > 0)\n .slice(0, 500);\n\n if (validIds.length === 0) return [];\n\n const placeholders = validIds.map(() => '?').join(',');\n const sql = `SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch DESC, id DESC`;\n const stmt = db.query(sql);\n return stmt.all(...validIds) as Observation[];\n}\n\n/**\n * Timeline: chronological context around an observation\n */\nexport function getTimeline(\n db: Database,\n anchorId: number,\n depthBefore: number = 5,\n depthAfter: number = 5\n): TimelineEntry[] {\n // Find the anchor's epoch\n const anchorStmt = db.query('SELECT created_at_epoch FROM observations WHERE id = ?');\n const anchor = anchorStmt.get(anchorId) as { created_at_epoch: number } | null;\n\n if (!anchor) return [];\n\n const anchorEpoch = anchor.created_at_epoch;\n\n // Observations before (same epoch with smaller id, or earlier epoch)\n const beforeStmt = db.query(`\n SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch\n FROM observations\n WHERE (created_at_epoch < ? OR (created_at_epoch = ? AND id < ?))\n ORDER BY created_at_epoch DESC, id DESC\n LIMIT ?\n `);\n const before = (beforeStmt.all(anchorEpoch, anchorEpoch, anchorId, depthBefore) as TimelineEntry[]).reverse();\n\n // The anchor itself\n const selfStmt = db.query(`\n SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch\n FROM observations WHERE id = ?\n `);\n const self = selfStmt.all(anchorId) as TimelineEntry[];\n\n // Observations after (same epoch with larger id, or later epoch)\n const afterStmt = db.query(`\n SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch\n FROM observations\n WHERE (created_at_epoch > ? OR (created_at_epoch = ? AND id > ?))\n ORDER BY created_at_epoch ASC, id ASC\n LIMIT ?\n `);\n const after = afterStmt.all(anchorEpoch, anchorEpoch, anchorId, depthAfter) as TimelineEntry[];\n\n return [...before, ...self, ...after];\n}\n\n/**\n * Database statistics for a project.\n * Single CTE query replacing 6 separate queries.\n */\nexport function getProjectStats(db: Database, project: string): {\n observations: number;\n summaries: number;\n sessions: number;\n prompts: number;\n tokenEconomics: { discoveryTokens: number; readTokens: number; savings: number };\n} {\n const sql = `\n WITH\n obs_stats AS (\n SELECT\n COUNT(*) as count,\n COALESCE(SUM(discovery_tokens), 0) as discovery_tokens,\n COALESCE(SUM(\n CAST((LENGTH(COALESCE(title, '')) + LENGTH(COALESCE(narrative, ''))) / 4 AS INTEGER)\n ), 0) as read_tokens\n FROM observations WHERE project = ?\n ),\n sum_count AS (SELECT COUNT(*) as count FROM summaries WHERE project = ?),\n ses_count AS (SELECT COUNT(*) as count FROM sessions WHERE project = ?),\n prm_count AS (SELECT COUNT(*) as count FROM prompts WHERE project = ?)\n SELECT\n obs_stats.count as observations,\n obs_stats.discovery_tokens,\n obs_stats.read_tokens,\n sum_count.count as summaries,\n ses_count.count as sessions,\n prm_count.count as prompts\n FROM obs_stats, sum_count, ses_count, prm_count\n `;\n\n const row = db.query(sql).get(project, project, project, project) as any;\n\n const discoveryTokens = row?.discovery_tokens || 0;\n const readTokens = row?.read_tokens || 0;\n const savings = Math.max(0, discoveryTokens - readTokens);\n\n return {\n observations: row?.observations || 0,\n summaries: row?.summaries || 0,\n sessions: row?.sessions || 0,\n prompts: row?.prompts || 0,\n tokenEconomics: { discoveryTokens, readTokens, savings },\n };\n}\n\n/**\n * Find observations with files modified after the observation was created.\n * Checks the filesystem mtime for each file in files_modified.\n */\nexport function getStaleObservations(db: Database, project: string): Observation[] {\n // Query observations with non-empty files_modified\n const rows = db.query(`\n SELECT * FROM observations\n WHERE project = ? AND files_modified IS NOT NULL AND files_modified != ''\n ORDER BY created_at_epoch DESC, id DESC\n LIMIT 500\n `).all(project) as Observation[];\n\n const staleObs: Observation[] = [];\n\n for (const obs of rows) {\n if (!obs.files_modified) continue;\n\n // Parse files_modified (comma-separated)\n const files = obs.files_modified.split(',').map(f => f.trim()).filter(Boolean);\n\n let isStale = false;\n for (const filepath of files) {\n try {\n if (!existsSync(filepath)) continue; // File removed, cannot verify\n const stat = statSync(filepath);\n if (stat.mtimeMs > obs.created_at_epoch) {\n isStale = true;\n break;\n }\n } catch {\n // File not accessible, skip\n }\n }\n\n if (isStale) {\n staleObs.push(obs);\n }\n }\n\n return staleObs;\n}\n\n/**\n * Mark observations as stale (1) or fresh (0) in the database.\n */\nexport function markObservationsStale(db: Database, ids: number[], stale: boolean): void {\n if (!Array.isArray(ids) || ids.length === 0) return;\n\n const validIds = ids\n .filter(id => typeof id === 'number' && Number.isInteger(id) && id > 0)\n .slice(0, 500);\n\n if (validIds.length === 0) return;\n\n const placeholders = validIds.map(() => '?').join(',');\n db.run(\n `UPDATE observations SET is_stale = ? WHERE id IN (${placeholders})`,\n [stale ? 1 : 0, ...validIds]\n );\n}\n", "/**\n * Secret filtering module.\n * Detects and redacts sensitive values (API keys, tokens, passwords)\n * before they are persisted to the database.\n */\n\nconst SECRET_PATTERNS: Array<{ name: string; pattern: RegExp }> = [\n // AWS Access Keys (AKIA, ABIA, ACCA, ASIA prefixes + 16 alphanumeric chars)\n { name: 'aws-key', pattern: /(?:AKIA|ABIA|ACCA|ASIA)[A-Z0-9]{16}/g },\n // JWT tokens (three base64url segments separated by dots)\n { name: 'jwt', pattern: /eyJ[a-zA-Z0-9_-]{10,}\\.eyJ[a-zA-Z0-9_-]{10,}\\.[a-zA-Z0-9_-]{10,}/g },\n // Generic API keys in key=value or key: value assignments\n { name: 'api-key', pattern: /(?:api[_-]?key|apikey|api[_-]?secret)\\s*[:=]\\s*['\"]?([a-zA-Z0-9_\\-]{20,})['\"]?/gi },\n // Password/secret/token in variable assignments\n { name: 'credential', pattern: /(?:password|passwd|pwd|secret|token|auth[_-]?token|access[_-]?token|bearer)\\s*[:=]\\s*['\"]?([^\\s'\"]{8,})['\"]?/gi },\n // Credentials embedded in URLs (user:pass@host)\n { name: 'url-credential', pattern: /(?:https?:\\/\\/)([^:]+):([^@]+)@/g },\n // PEM-encoded private keys (RSA, EC, DSA, OpenSSH)\n { name: 'private-key', pattern: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g },\n // GitHub personal access tokens (ghp_, gho_, ghu_, ghs_, ghr_ prefixes)\n { name: 'github-token', pattern: /gh[pousr]_[a-zA-Z0-9]{36,}/g },\n // Slack bot/user/app tokens\n { name: 'slack-token', pattern: /xox[bpoas]-[a-zA-Z0-9-]{10,}/g },\n // HTTP Authorization Bearer header values\n { name: 'bearer-header', pattern: /\\bBearer\\s+([a-zA-Z0-9_\\-\\.]{20,})/g },\n // Generic hex secrets (32+ hex chars after a key/secret/token/password label)\n { name: 'hex-secret', pattern: /(?:key|secret|token|password)\\s*[:=]\\s*['\"]?([0-9a-f]{32,})['\"]?/gi },\n];\n\n/**\n * Redact detected secrets from text.\n * Preserves the first 4 characters of each match for debugging context,\n * then appends ***REDACTED*** in place of the sensitive portion.\n *\n * @param text - The string to sanitize\n * @returns The sanitized string with secrets replaced\n */\nexport function redactSecrets(text: string): string {\n if (!text) return text;\n\n let redacted = text;\n for (const { pattern } of SECRET_PATTERNS) {\n // Reset lastIndex so global regexes work correctly on every call\n pattern.lastIndex = 0;\n redacted = redacted.replace(pattern, (match) => {\n const prefix = match.substring(0, Math.min(4, match.length));\n return `${prefix}***REDACTED***`;\n });\n }\n return redacted;\n}\n\n/**\n * Check whether text contains any recognizable secret patterns.\n * Useful as a fast guard before triggering heavier processing.\n *\n * @param text - The string to inspect\n * @returns true if at least one secret pattern is found\n */\nexport function containsSecrets(text: string): boolean {\n if (!text) return false;\n\n for (const { pattern } of SECRET_PATTERNS) {\n pattern.lastIndex = 0;\n if (pattern.test(text)) return true;\n }\n return false;\n}\n", "/**\n * Automatic observation categorizer.\n * Assigns semantic categories based on keyword rules.\n * Categories: architecture, debugging, refactoring, feature-dev, testing, docs, config, security, general\n */\n\nexport type ObservationCategory =\n | 'architecture'\n | 'debugging'\n | 'refactoring'\n | 'feature-dev'\n | 'testing'\n | 'docs'\n | 'config'\n | 'security'\n | 'general';\n\ninterface CategoryRule {\n category: ObservationCategory;\n /** Keywords in title/text that strongly indicate this category */\n keywords: string[];\n /** Observation types that indicate this category */\n types?: string[];\n /** File patterns in files_modified/files_read */\n filePatterns?: RegExp[];\n /** Priority weight (higher = wins in tie) */\n weight: number;\n}\n\nconst CATEGORY_RULES: CategoryRule[] = [\n {\n category: 'security',\n keywords: [\n 'security', 'vulnerability', 'cve', 'xss', 'csrf', 'injection',\n 'sanitize', 'escape', 'auth', 'authentication', 'authorization',\n 'permission', 'helmet', 'cors', 'rate-limit', 'token', 'encrypt',\n 'decrypt', 'secret', 'redact', 'owasp',\n ],\n filePatterns: [/security/i, /auth/i, /secrets?\\.ts/i],\n weight: 10,\n },\n {\n category: 'testing',\n keywords: [\n 'test', 'spec', 'expect', 'assert', 'mock', 'stub', 'fixture',\n 'coverage', 'jest', 'vitest', 'bun test', 'unit test',\n 'integration test', 'e2e',\n ],\n types: ['test'],\n filePatterns: [/\\.test\\./i, /\\.spec\\./i, /tests?\\//i, /__tests__/i],\n weight: 8,\n },\n {\n category: 'debugging',\n keywords: [\n 'debug', 'fix', 'bug', 'error', 'crash', 'stacktrace', 'stack trace',\n 'exception', 'breakpoint', 'investigate', 'root cause', 'troubleshoot',\n 'diagnose', 'bisect', 'regression',\n ],\n types: ['bugfix'],\n weight: 8,\n },\n {\n category: 'architecture',\n keywords: [\n 'architect', 'design', 'pattern', 'modular', 'migration',\n 'schema', 'database', 'api design', 'abstract',\n 'dependency injection', 'singleton', 'factory', 'observer', 'middleware',\n 'pipeline', 'microservice', 'monolith',\n ],\n types: ['decision', 'constraint'],\n weight: 7,\n },\n {\n category: 'refactoring',\n keywords: [\n 'refactor', 'rename', 'extract', 'inline', 'move', 'split', 'merge',\n 'simplify', 'cleanup', 'clean up', 'dead code', 'consolidate',\n 'reorganize', 'restructure', 'decouple',\n ],\n weight: 6,\n },\n {\n category: 'config',\n keywords: [\n 'config', 'configuration', 'env', 'environment', 'dotenv', '.env',\n 'settings', 'tsconfig', 'eslint', 'prettier', 'webpack', 'vite',\n 'esbuild', 'docker', 'ci/cd', 'github actions', 'deploy', 'build',\n 'bundle', 'package.json',\n ],\n filePatterns: [\n /\\.config\\./i, /\\.env/i, /tsconfig/i, /\\.ya?ml/i,\n /Dockerfile/i, /docker-compose/i,\n ],\n weight: 5,\n },\n {\n category: 'docs',\n keywords: [\n 'document', 'readme', 'changelog', 'jsdoc', 'comment', 'explain',\n 'guide', 'tutorial', 'api doc', 'openapi', 'swagger',\n ],\n types: ['docs'],\n filePatterns: [/\\.md$/i, /docs?\\//i, /readme/i, /changelog/i],\n weight: 5,\n },\n {\n category: 'feature-dev',\n keywords: [\n 'feature', 'implement', 'add', 'create', 'new', 'endpoint', 'component',\n 'module', 'service', 'handler', 'route', 'hook', 'plugin', 'integration',\n ],\n types: ['feature', 'file-write'],\n weight: 3, // lowest \u2014 generic catch-all for development\n },\n];\n\n/**\n * Categorize an observation based on its content.\n * Returns the best matching category, or 'general' if no strong match.\n */\nexport function categorize(input: {\n type: string;\n title: string;\n text?: string | null;\n narrative?: string | null;\n concepts?: string | null;\n filesModified?: string | null;\n filesRead?: string | null;\n}): ObservationCategory {\n const scores: Map<ObservationCategory, number> = new Map();\n\n // Combine searchable text (lowercase for case-insensitive matching)\n const searchText = [\n input.title,\n input.text || '',\n input.narrative || '',\n input.concepts || '',\n ].join(' ').toLowerCase();\n\n const allFiles = [input.filesModified || '', input.filesRead || ''].join(',');\n\n for (const rule of CATEGORY_RULES) {\n let score = 0;\n\n // Keyword matching (each match adds the rule's weight)\n for (const kw of rule.keywords) {\n if (searchText.includes(kw.toLowerCase())) {\n score += rule.weight;\n }\n }\n\n // Type matching (strong signal)\n if (rule.types && rule.types.includes(input.type)) {\n score += rule.weight * 2;\n }\n\n // File pattern matching\n if (rule.filePatterns && allFiles) {\n for (const pattern of rule.filePatterns) {\n if (pattern.test(allFiles)) {\n score += rule.weight;\n }\n }\n }\n\n if (score > 0) {\n scores.set(rule.category, (scores.get(rule.category) || 0) + score);\n }\n }\n\n // Find the highest scoring category\n let bestCategory: ObservationCategory = 'general';\n let bestScore = 0;\n\n for (const [category, score] of scores) {\n if (score > bestScore) {\n bestScore = score;\n bestCategory = category;\n }\n }\n\n return bestCategory;\n}\n\n/**\n * Get all available categories.\n */\nexport function getCategories(): ObservationCategory[] {\n return [\n 'architecture', 'debugging', 'refactoring', 'feature-dev',\n 'testing', 'docs', 'config', 'security', 'general',\n ];\n}\n", "import { Database } from 'bun:sqlite';\nimport type { Observation } from '../../types/worker-types.js';\nimport { redactSecrets } from '../../utils/secrets.js';\nimport { categorize } from '../../utils/categorizer.js';\n\n/**\n * Observation operations for Kiro Memory database\n */\n\n/** Escape LIKE wildcard characters to prevent pattern injection */\nfunction escapeLikePattern(input: string): string {\n return input.replace(/[%_\\\\]/g, '\\\\$&');\n}\n\n/**\n * Check if an observation with the same content_hash exists within the last 30 seconds.\n * Returns true if it is a duplicate (to be discarded).\n */\nexport function isDuplicateObservation(db: Database, contentHash: string, windowMs: number = 30_000): boolean {\n if (!contentHash) return false;\n const threshold = Date.now() - windowMs;\n const result = db.query(\n 'SELECT id FROM observations WHERE content_hash = ? AND created_at_epoch > ? LIMIT 1'\n ).get(contentHash, threshold);\n return !!result;\n}\n\nexport function createObservation(\n db: Database,\n memorySessionId: string,\n project: string,\n type: string,\n title: string,\n subtitle: string | null,\n text: string | null,\n narrative: string | null,\n facts: string | null,\n concepts: string | null,\n filesRead: string | null,\n filesModified: string | null,\n promptNumber: number,\n contentHash: string | null = null,\n discoveryTokens: number = 0\n): number {\n const now = new Date();\n\n // Safety net: redact any secrets that may have slipped through upstream layers\n const safeTitle = redactSecrets(title);\n const safeText = text ? redactSecrets(text) : text;\n const safeNarrative = narrative ? redactSecrets(narrative) : narrative;\n\n // Assign automatic semantic category based on content keywords\n const autoCategory = categorize({\n type,\n title: safeTitle,\n text: safeText,\n narrative: safeNarrative,\n concepts,\n filesModified,\n filesRead,\n });\n\n const result = db.run(\n `INSERT INTO observations\n (memory_session_id, project, type, title, subtitle, text, narrative, facts, concepts, files_read, files_modified, prompt_number, created_at, created_at_epoch, content_hash, discovery_tokens, auto_category)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [memorySessionId, project, type, safeTitle, subtitle, safeText, safeNarrative, facts, concepts, filesRead, filesModified, promptNumber, now.toISOString(), now.getTime(), contentHash, discoveryTokens, autoCategory]\n );\n return Number(result.lastInsertRowid);\n}\n\nexport function getObservationsBySession(db: Database, memorySessionId: string): Observation[] {\n const query = db.query(\n 'SELECT * FROM observations WHERE memory_session_id = ? ORDER BY prompt_number ASC'\n );\n return query.all(memorySessionId) as Observation[];\n}\n\nexport function getObservationsByProject(db: Database, project: string, limit: number = 100): Observation[] {\n const query = db.query(\n 'SELECT * FROM observations WHERE project = ? ORDER BY created_at_epoch DESC, id DESC LIMIT ?'\n );\n return query.all(project, limit) as Observation[];\n}\n\nexport function searchObservations(db: Database, searchTerm: string, project?: string): Observation[] {\n const sql = project\n ? `SELECT * FROM observations\n WHERE project = ? AND (title LIKE ? ESCAPE '\\\\' OR text LIKE ? ESCAPE '\\\\' OR narrative LIKE ? ESCAPE '\\\\')\n ORDER BY created_at_epoch DESC, id DESC`\n : `SELECT * FROM observations\n WHERE title LIKE ? ESCAPE '\\\\' OR text LIKE ? ESCAPE '\\\\' OR narrative LIKE ? ESCAPE '\\\\'\n ORDER BY created_at_epoch DESC, id DESC`;\n\n const pattern = `%${escapeLikePattern(searchTerm)}%`;\n const query = db.query(sql);\n\n if (project) {\n return query.all(project, pattern, pattern, pattern) as Observation[];\n }\n return query.all(pattern, pattern, pattern) as Observation[];\n}\n\nexport function deleteObservation(db: Database, id: number): void {\n db.run('DELETE FROM observations WHERE id = ?', [id]);\n}\n\n/**\n * Update the last access timestamp for observations found in search.\n * Fire-and-forget: non-blocking, ignores errors.\n */\nexport function updateLastAccessed(db: Database, ids: number[]): void {\n if (!Array.isArray(ids) || ids.length === 0) return;\n\n const validIds = ids\n .filter(id => typeof id === 'number' && Number.isInteger(id) && id > 0)\n .slice(0, 500);\n\n if (validIds.length === 0) return;\n\n const now = Date.now();\n const placeholders = validIds.map(() => '?').join(',');\n db.run(\n `UPDATE observations SET last_accessed_epoch = ? WHERE id IN (${placeholders})`,\n [now, ...validIds]\n );\n}\n\n/**\n * Consolidate duplicate observations on the same file and type.\n * Groups by (project, type, files_modified), keeps the most recent,\n * concatenates unique contents, deletes the old ones.\n *\n * Fix: counts calculated inside the transaction and returned directly,\n * dry-run separated from the transaction to avoid unnecessary locks.\n */\nexport function consolidateObservations(\n db: Database,\n project: string,\n options: { dryRun?: boolean; minGroupSize?: number } = {}\n): { merged: number; removed: number } {\n const minGroupSize = options.minGroupSize || 3;\n\n // Find groups of observations with the same (project, type, files_modified)\n const groups = db.query(`\n SELECT type, files_modified, COUNT(*) as cnt, GROUP_CONCAT(id) as ids\n FROM observations\n WHERE project = ? AND files_modified IS NOT NULL AND files_modified != ''\n GROUP BY type, files_modified\n HAVING cnt >= ?\n ORDER BY cnt DESC\n `).all(project, minGroupSize) as Array<{\n type: string;\n files_modified: string;\n cnt: number;\n ids: string;\n }>;\n\n if (groups.length === 0) return { merged: 0, removed: 0 };\n\n // Dry-run: calculate counts without opening a transaction\n if (options.dryRun) {\n let totalMerged = 0;\n let totalRemoved = 0;\n\n for (const group of groups) {\n const obsIds = group.ids.split(',').map(Number);\n const placeholders = obsIds.map(() => '?').join(',');\n const count = (db.query(\n `SELECT COUNT(*) as cnt FROM observations WHERE id IN (${placeholders})`\n ).get(...obsIds) as { cnt: number })?.cnt || 0;\n\n if (count >= minGroupSize) {\n totalMerged += 1;\n totalRemoved += count - 1;\n }\n }\n\n return { merged: totalMerged, removed: totalRemoved };\n }\n\n // Execute consolidation in an atomic transaction.\n // Counts are calculated and returned by the transaction itself,\n // so if it fails no partial values remain.\n const runConsolidation = db.transaction(() => {\n let merged = 0;\n let removed = 0;\n\n for (const group of groups) {\n const obsIds = group.ids.split(',').map(Number);\n const placeholders = obsIds.map(() => '?').join(',');\n const observations = db.query(\n `SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch DESC, id DESC`\n ).all(...obsIds) as Observation[];\n\n if (observations.length < minGroupSize) continue;\n\n // Keep the most recent, concatenate unique contents from the others\n const keeper = observations[0];\n const others = observations.slice(1);\n\n const uniqueTexts = new Set<string>();\n if (keeper.text) uniqueTexts.add(keeper.text);\n for (const obs of others) {\n if (obs.text && !uniqueTexts.has(obs.text)) {\n uniqueTexts.add(obs.text);\n }\n }\n\n // Update the keeper with consolidated text\n const consolidatedText = Array.from(uniqueTexts).join('\\n---\\n').substring(0, 100_000);\n db.run(\n 'UPDATE observations SET text = ?, title = ? WHERE id = ?',\n [consolidatedText, `[consolidated x${observations.length}] ${keeper.title}`, keeper.id]\n );\n\n // Delete old observations (and their embeddings)\n const removeIds = others.map(o => o.id);\n const removePlaceholders = removeIds.map(() => '?').join(',');\n db.run(`DELETE FROM observations WHERE id IN (${removePlaceholders})`, removeIds);\n db.run(`DELETE FROM observation_embeddings WHERE observation_id IN (${removePlaceholders})`, removeIds);\n\n merged += 1;\n removed += removeIds.length;\n }\n\n return { merged, removed };\n });\n\n return runConsolidation();\n}\n", "/**\n * Kiro Memory Worker Service\n *\n * Lean orchestrator: configures Express, mounts modular routers,\n * manages lifecycle (PID, shutdown, error handling).\n *\n * Routes defined in src/services/routes/:\n * core.ts \u2014 health, SSE, notify\n * observations.ts \u2014 CRUD observations, knowledge, memory save\n * summaries.ts \u2014 CRUD summaries\n * search.ts \u2014 FTS5, hybrid search, timeline\n * analytics.ts \u2014 overview, timeline, types, sessions\n * sessions.ts \u2014 sessions, checkpoint, prompts\n * projects.ts \u2014 project list, aliases, stats\n * data.ts \u2014 embeddings, retention, export, report\n * backup.ts \u2014 backup create, list, restore\n */\n\nimport express from 'express';\nimport cors from 'cors';\nimport helmet from 'helmet';\nimport rateLimit from 'express-rate-limit';\nimport crypto from 'crypto';\nimport { join, dirname } from 'path';\nimport { existsSync, mkdirSync, writeFileSync, unlinkSync, chmodSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { KiroMemoryDatabase } from './sqlite/Database.js';\nimport { getHybridSearch } from './search/HybridSearch.js';\nimport { createWorkerContext, getClients } from './worker-context.js';\nimport { applyRetention, buildRetentionConfig } from './sqlite/Retention.js';\nimport { createBackup, rotateBackups } from './sqlite/Backup.js';\nimport { listConfig, getConfigValue } from '../cli/cli-utils.js';\nimport { logger } from '../utils/logger.js';\nimport { DATA_DIR, DB_PATH, BACKUPS_DIR } from '../shared/paths.js';\n\n// Plugin system\nimport { PluginLoader } from './plugins/PluginLoader.js';\nimport { PluginRegistry } from './plugins/PluginRegistry.js';\n\n// Modular routers\nimport { createCoreRouter } from './routes/core.js';\nimport { createObservationsRouter } from './routes/observations.js';\nimport { createSummariesRouter } from './routes/summaries.js';\nimport { createSearchRouter } from './routes/search.js';\nimport { createAnalyticsRouter } from './routes/analytics.js';\nimport { createSessionsRouter } from './routes/sessions.js';\nimport { createProjectsRouter } from './routes/projects.js';\nimport { createDataRouter } from './routes/data.js';\n// Router webhook GitHub\nimport { createWebhooksRouter } from './routes/webhooks.js';\n// Router import/export JSONL\nimport { createImportExportRouter } from './routes/importexport.js';\n// Router documentazione OpenAPI\nimport { createDocsRouter } from './openapi/index.js';\n// Router backup database\nimport { createBackupRouter } from './routes/backup.js';\n// Router plugin system\nimport { createPluginsRouter } from './routes/plugins.js';\n\n// \u2500\u2500 Configuration \u2500\u2500\n\nconst __worker_dirname = dirname(fileURLToPath(import.meta.url));\nconst PORT = process.env.KIRO_MEMORY_WORKER_PORT || process.env.CONTEXTKIT_WORKER_PORT || 3001;\nconst HOST = process.env.KIRO_MEMORY_WORKER_HOST || process.env.CONTEXTKIT_WORKER_HOST || '127.0.0.1';\nconst PID_FILE = join(DATA_DIR, 'worker.pid');\nconst TOKEN_FILE = join(DATA_DIR, 'worker.token');\n\n// \u2500\u2500 Initialization \u2500\u2500\n\nif (!existsSync(DATA_DIR)) {\n mkdirSync(DATA_DIR, { recursive: true });\n}\n\n// Authentication token for hook \u2192 worker communication\nconst WORKER_TOKEN = crypto.randomBytes(32).toString('hex');\nwriteFileSync(TOKEN_FILE, WORKER_TOKEN, 'utf-8');\ntry {\n chmodSync(TOKEN_FILE, 0o600);\n} catch (err) {\n if (process.platform !== 'win32') {\n logger.warn('WORKER', `chmod 600 failed on ${TOKEN_FILE}`, {}, err as Error);\n }\n}\n\n// Database\nconst db = new KiroMemoryDatabase();\nlogger.info('WORKER', 'Database initialized');\n\n// Embedding service (lazy, non bloccante)\ngetHybridSearch().initialize().catch(err => {\n logger.warn('WORKER', 'Embedding initialization failed, FTS5 search only', {}, err as Error);\n});\n\n// Shared context for all routers\nconst ctx = createWorkerContext(db);\n\n// \u2500\u2500 Express app \u2500\u2500\n\nconst app = express();\n\n// Security: protective HTTP headers\napp.use(helmet({\n contentSecurityPolicy: {\n directives: {\n defaultSrc: [\"'self'\"],\n scriptSrc: [\"'self'\", \"'unsafe-inline'\"],\n styleSrc: [\"'self'\", \"'unsafe-inline'\", \"https://fonts.googleapis.com\"],\n imgSrc: [\"'self'\", \"data:\"],\n connectSrc: [\"'self'\"],\n fontSrc: [\"'self'\", \"https://fonts.gstatic.com\"],\n frameSrc: [\"'none'\"]\n }\n }\n}));\n\n// CORS restricted to localhost\napp.use(cors({\n origin: [\n `http://localhost:${PORT}`,\n `http://127.0.0.1:${PORT}`,\n `http://${HOST}:${PORT}`\n ],\n credentials: true,\n maxAge: 86400\n}));\n\n// Body size limit: 50MB (per supportare import JSONL grandi)\napp.use(express.json({ limit: '50mb' }));\n// Supporto text/plain per endpoint import JSONL\napp.use(express.text({ limit: '50mb', type: 'text/plain' }));\n\n// Global rate limiting: 200 req/min per IP\napp.use('/api/', rateLimit({\n windowMs: 60_000,\n max: 200,\n standardHeaders: true,\n legacyHeaders: false,\n message: { error: 'Too many requests, retry later' }\n}));\n\n// \u2500\u2500 Mount modular routers \u2500\u2500\n\napp.use(createCoreRouter(ctx, WORKER_TOKEN));\napp.use(createObservationsRouter(ctx));\napp.use(createSummariesRouter(ctx));\napp.use(createSearchRouter(ctx));\napp.use(createAnalyticsRouter(ctx));\napp.use(createSessionsRouter(ctx));\napp.use(createProjectsRouter(ctx));\napp.use(createDataRouter(ctx, WORKER_TOKEN));\n// Webhook GitHub e API query link\napp.use(createWebhooksRouter(ctx));\n// Import/export JSONL\napp.use(createImportExportRouter(ctx));\n// Documentazione OpenAPI interattiva (Swagger UI + spec JSON)\napp.use(createDocsRouter());\n// Backup database\napp.use(createBackupRouter(ctx, WORKER_TOKEN));\n// Plugin system\napp.use(createPluginsRouter(ctx));\n\n// \u2500\u2500 Static files and viewer \u2500\u2500\n\napp.use(express.static(__worker_dirname, {\n index: false,\n maxAge: '1h'\n}));\n\napp.get('/', (_req, res) => {\n const viewerPath = join(__worker_dirname, 'viewer.html');\n if (existsSync(viewerPath)) {\n res.sendFile(viewerPath);\n } else {\n res.status(404).json({ error: 'Viewer not found. Run npm run build first.' });\n }\n});\n\n// \u2500\u2500 Job schedulato: cleanup retention automatico \u2500\u2500\n\n/**\n * Avvia il cleanup retention periodico se abilitato nella configurazione.\n * Usa setInterval con intervallo configurabile (default: 24 ore).\n * Il primo run avviene 30 secondi dopo lo startup per non rallentare l'avvio.\n */\nfunction scheduleRetentionCleanup(): void {\n const enabled = getConfigValue('retention.autoCleanupEnabled');\n if (!enabled) {\n logger.info('WORKER', 'Retention automatico disabilitato (retention.autoCleanupEnabled=false)');\n return;\n }\n\n const intervalHours = Number(getConfigValue('retention.autoCleanupIntervalHours') ?? 24);\n const intervalMs = intervalHours * 3_600_000;\n\n // Funzione di cleanup riutilizzabile\n function runRetentionCleanup(): void {\n try {\n const config = buildRetentionConfig(listConfig());\n const result = applyRetention(db.db, config);\n if (result.total > 0) {\n logger.info('WORKER', `Retention schedulata: ${result.total} record eliminati (obs=${result.observations}, sum=${result.summaries}, prompts=${result.prompts}, knowledge=${result.knowledge})`);\n } else {\n logger.debug('WORKER', 'Retention schedulata: nessun record da eliminare');\n }\n } catch (err) {\n logger.error('WORKER', 'Retention schedulata fallita', {}, err as Error);\n }\n }\n\n logger.info('WORKER', `Retention automatica attiva (ogni ${intervalHours}h)`);\n\n // Esegui alla prima occasione con un ritardo iniziale di 30s per stabilizzare il server\n const startupDelay = setTimeout(runRetentionCleanup, 30_000);\n\n // Poi esegui periodicamente all'intervallo configurato\n const retentionInterval = setInterval(runRetentionCleanup, intervalMs);\n\n // Pulisci i timer al termine del processo\n process.once('beforeExit', () => {\n clearTimeout(startupDelay);\n clearInterval(retentionInterval);\n });\n}\n\nscheduleRetentionCleanup();\n\n// \u2500\u2500 Job schedulato: backup automatico \u2500\u2500\n\n/**\n * Avvia il backup automatico periodico se abilitato nella configurazione.\n * Usa setInterval con intervallo configurabile (default: 24 ore).\n * Il primo run avviene 60 secondi dopo lo startup.\n */\nfunction scheduleBackupJob(): void {\n const enabled = getConfigValue('backup.enabled');\n if (!enabled) {\n logger.info('WORKER', 'Backup automatico disabilitato (backup.enabled=false)');\n return;\n }\n\n const intervalHours = Number(getConfigValue('backup.intervalHours') ?? 24);\n const maxKeep = Number(getConfigValue('backup.maxKeep') ?? 7);\n const intervalMs = intervalHours * 3_600_000;\n\n // Funzione di backup riutilizzabile\n function runBackup(): void {\n try {\n const entry = createBackup(DB_PATH, BACKUPS_DIR, db.db);\n logger.info('WORKER', `Backup schedulato creato: ${entry.metadata.filename} (obs=${entry.metadata.stats.observations})`);\n\n // Rotazione automatica dopo ogni backup\n const deleted = rotateBackups(BACKUPS_DIR, maxKeep);\n if (deleted > 0) {\n logger.info('WORKER', `Rotazione backup: ${deleted} file rimossi, ${maxKeep} mantenuti`);\n }\n } catch (err) {\n logger.error('WORKER', 'Backup schedulato fallito', {}, err as Error);\n }\n }\n\n logger.info('WORKER', `Backup automatico attivo (ogni ${intervalHours}h, max ${maxKeep} backup)`);\n\n // Prima esecuzione 60 secondi dopo lo startup (dopo la retention)\n const startupDelay = setTimeout(runBackup, 60_000);\n\n // Poi esegui periodicamente all'intervallo configurato\n const backupInterval = setInterval(runBackup, intervalMs);\n\n // Pulisci i timer al termine del processo\n process.once('beforeExit', () => {\n clearTimeout(startupDelay);\n clearInterval(backupInterval);\n });\n}\n\nscheduleBackupJob();\n\n// \u2500\u2500 Plugin system: discovery e caricamento \u2500\u2500\n\n/**\n * Avvia il plugin loader in modo asincrono e non bloccante.\n * I plugin vengono scoperti e caricati dopo lo startup del server\n * per non ritardare l'avvio. Gli errori di singoli plugin sono isolati.\n */\nasync function initializePlugins(): Promise<void> {\n try {\n const registry = PluginRegistry.getInstance();\n\n // Adattatore IPluginRegistry (duck-type per PluginLoader)\n // Il PluginLoader registra plugin via IPlugin del PluginLoader (con PluginContext locale).\n // Questi plugin vengono poi abilitati dal PluginRegistry singleton con il contesto SDK.\n const loaderRegistryAdapter = {\n register: (p: any) => {\n try { registry.register(p as any); }\n catch { /* plugin gi\u00E0 registrato, ignora */ }\n },\n unregister: (name: string) => { registry.unregister(name).catch(() => {}); },\n get: (name: string) => registry.get(name) as any\n };\n\n const pluginLoader = new PluginLoader(loaderRegistryAdapter as any);\n const result = await pluginLoader.loadAll();\n\n if (result.loaded.length > 0) {\n logger.info('WORKER', `Plugin caricati: ${result.loaded.join(', ')}`);\n }\n if (result.failed.length > 0) {\n for (const { name, error } of result.failed) {\n logger.warn('WORKER', `Plugin \"${name}\" non caricato: ${error}`);\n }\n }\n } catch (err) {\n // Errore non fatale: il worker continua senza plugin\n logger.warn('WORKER', 'Inizializzazione plugin system fallita', {}, err as Error);\n }\n}\n\n// \u2500\u2500 Server startup \u2500\u2500\n\nconst server = app.listen(Number(PORT), HOST, () => {\n logger.info('WORKER', `Kiro Memory worker started on http://${HOST}:${PORT}`);\n writeFileSync(PID_FILE, String(process.pid), 'utf-8');\n\n // Avvia il plugin system in background dopo che il server \u00E8 attivo.\n // Non bloccante: errori di plugin non impattano il server.\n initializePlugins().catch(err => {\n logger.warn('WORKER', 'Plugin system: errore non gestito', {}, err as Error);\n });\n});\n\n// \u2500\u2500 Graceful shutdown \u2500\u2500\n\nfunction shutdown(signal: string): void {\n logger.info('WORKER', `Received ${signal}, shutting down...`);\n\n // Close all SSE clients to unblock server.close()\n const sseClients = getClients();\n for (const client of sseClients) {\n try { client.end(); } catch { /* ignora errori su client gi\u00E0 chiusi */ }\n }\n\n // Force timeout: if server.close() doesn't complete in 5s, force exit\n const forceTimeout = setTimeout(() => {\n logger.warn('WORKER', 'Forced shutdown after 5s timeout');\n if (existsSync(PID_FILE)) unlinkSync(PID_FILE);\n db.close();\n process.exit(1);\n }, 5000);\n\n server.close(() => {\n clearTimeout(forceTimeout);\n logger.info('WORKER', 'Server closed');\n\n if (existsSync(PID_FILE)) {\n unlinkSync(PID_FILE);\n }\n\n db.close();\n process.exit(0);\n });\n}\n\nprocess.on('SIGTERM', () => shutdown('SIGTERM'));\nprocess.on('SIGINT', () => shutdown('SIGINT'));\n\nprocess.on('uncaughtException', (error) => {\n logger.error('WORKER', 'Uncaught exception', {}, error);\n shutdown('uncaughtException');\n});\n\nprocess.on('unhandledRejection', (reason) => {\n logger.error('WORKER', 'Unhandled promise rejection', { reason }, reason as Error);\n});\n", "const dangerouslyDisableDefaultSrc = Symbol(\"dangerouslyDisableDefaultSrc\")\nconst SHOULD_BE_QUOTED = new Set([\"none\", \"self\", \"strict-dynamic\", \"report-sample\", \"inline-speculation-rules\", \"unsafe-inline\", \"unsafe-eval\", \"unsafe-hashes\", \"wasm-unsafe-eval\"])\nconst getDefaultDirectives = () => ({\n\t\"default-src\": [\"'self'\"],\n\t\"base-uri\": [\"'self'\"],\n\t\"font-src\": [\"'self'\", \"https:\", \"data:\"],\n\t\"form-action\": [\"'self'\"],\n\t\"frame-ancestors\": [\"'self'\"],\n\t\"img-src\": [\"'self'\", \"data:\"],\n\t\"object-src\": [\"'none'\"],\n\t\"script-src\": [\"'self'\"],\n\t\"script-src-attr\": [\"'none'\"],\n\t\"style-src\": [\"'self'\", \"https:\", \"'unsafe-inline'\"],\n\t\"upgrade-insecure-requests\": []\n})\nconst dashify = str => str.replace(/[A-Z]/g, capitalLetter => \"-\" + capitalLetter.toLowerCase())\nconst assertDirectiveValueIsValid = (directiveName, directiveValue) => {\n\tif (/;|,/.test(directiveValue)) {\n\t\tthrow new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}`)\n\t}\n}\nconst assertDirectiveValueEntryIsValid = (directiveName, directiveValueEntry) => {\n\tif (SHOULD_BE_QUOTED.has(directiveValueEntry) || directiveValueEntry.startsWith(\"nonce-\") || directiveValueEntry.startsWith(\"sha256-\") || directiveValueEntry.startsWith(\"sha384-\") || directiveValueEntry.startsWith(\"sha512-\")) {\n\t\tthrow new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}. ${JSON.stringify(directiveValueEntry)} should be quoted`)\n\t}\n}\nfunction normalizeDirectives(options) {\n\tconst defaultDirectives = getDefaultDirectives()\n\tconst {useDefaults = true, directives: rawDirectives = defaultDirectives} = options\n\tconst result = new Map()\n\tconst directiveNamesSeen = new Set()\n\tconst directivesExplicitlyDisabled = new Set()\n\tfor (const rawDirectiveName in rawDirectives) {\n\t\tif (!Object.hasOwn(rawDirectives, rawDirectiveName)) {\n\t\t\tcontinue\n\t\t}\n\t\tif (rawDirectiveName.length === 0 || /[^a-zA-Z0-9-]/.test(rawDirectiveName)) {\n\t\t\tthrow new Error(`Content-Security-Policy received an invalid directive name ${JSON.stringify(rawDirectiveName)}`)\n\t\t}\n\t\tconst directiveName = dashify(rawDirectiveName)\n\t\tif (directiveNamesSeen.has(directiveName)) {\n\t\t\tthrow new Error(`Content-Security-Policy received a duplicate directive ${JSON.stringify(directiveName)}`)\n\t\t}\n\t\tdirectiveNamesSeen.add(directiveName)\n\t\tconst rawDirectiveValue = rawDirectives[rawDirectiveName]\n\t\tlet directiveValue\n\t\tif (rawDirectiveValue === null) {\n\t\t\tif (directiveName === \"default-src\") {\n\t\t\t\tthrow new Error(\"Content-Security-Policy needs a default-src but it was set to `null`. If you really want to disable it, set it to `contentSecurityPolicy.dangerouslyDisableDefaultSrc`.\")\n\t\t\t}\n\t\t\tdirectivesExplicitlyDisabled.add(directiveName)\n\t\t\tcontinue\n\t\t} else if (typeof rawDirectiveValue === \"string\") {\n\t\t\tdirectiveValue = [rawDirectiveValue]\n\t\t} else if (!rawDirectiveValue) {\n\t\t\tthrow new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}`)\n\t\t} else if (rawDirectiveValue === dangerouslyDisableDefaultSrc) {\n\t\t\tif (directiveName === \"default-src\") {\n\t\t\t\tdirectivesExplicitlyDisabled.add(\"default-src\")\n\t\t\t\tcontinue\n\t\t\t} else {\n\t\t\t\tthrow new Error(`Content-Security-Policy: tried to disable ${JSON.stringify(directiveName)} as if it were default-src; simply omit the key`)\n\t\t\t}\n\t\t} else {\n\t\t\tdirectiveValue = rawDirectiveValue\n\t\t}\n\t\tfor (const element of directiveValue) {\n\t\t\tif (typeof element !== \"string\") continue\n\t\t\tassertDirectiveValueIsValid(directiveName, element)\n\t\t\tassertDirectiveValueEntryIsValid(directiveName, element)\n\t\t}\n\t\tresult.set(directiveName, directiveValue)\n\t}\n\tif (useDefaults) {\n\t\tObject.entries(defaultDirectives).forEach(([defaultDirectiveName, defaultDirectiveValue]) => {\n\t\t\tif (!result.has(defaultDirectiveName) && !directivesExplicitlyDisabled.has(defaultDirectiveName)) {\n\t\t\t\tresult.set(defaultDirectiveName, defaultDirectiveValue)\n\t\t\t}\n\t\t})\n\t}\n\tif (!result.size) {\n\t\tthrow new Error(\"Content-Security-Policy has no directives. Either set some or disable the header\")\n\t}\n\tif (!result.has(\"default-src\") && !directivesExplicitlyDisabled.has(\"default-src\")) {\n\t\tthrow new Error(\"Content-Security-Policy needs a default-src but none was provided. If you really want to disable it, set it to `contentSecurityPolicy.dangerouslyDisableDefaultSrc`.\")\n\t}\n\treturn result\n}\nfunction getHeaderValue(req, res, normalizedDirectives) {\n\tconst result = []\n\tfor (const [directiveName, rawDirectiveValue] of normalizedDirectives) {\n\t\tlet directiveValue = \"\"\n\t\tfor (const element of rawDirectiveValue) {\n\t\t\tif (typeof element === \"function\") {\n\t\t\t\tconst newElement = element(req, res)\n\t\t\t\tassertDirectiveValueEntryIsValid(directiveName, newElement)\n\t\t\t\tdirectiveValue += \" \" + newElement\n\t\t\t} else {\n\t\t\t\tdirectiveValue += \" \" + element\n\t\t\t}\n\t\t}\n\t\tif (directiveValue) {\n\t\t\tassertDirectiveValueIsValid(directiveName, directiveValue)\n\t\t\tresult.push(`${directiveName}${directiveValue}`)\n\t\t} else {\n\t\t\tresult.push(directiveName)\n\t\t}\n\t}\n\treturn result.join(\";\")\n}\nconst contentSecurityPolicy = function contentSecurityPolicy(options = {}) {\n\tconst headerName = options.reportOnly ? \"Content-Security-Policy-Report-Only\" : \"Content-Security-Policy\"\n\tconst normalizedDirectives = normalizeDirectives(options)\n\treturn function contentSecurityPolicyMiddleware(req, res, next) {\n\t\tconst result = getHeaderValue(req, res, normalizedDirectives)\n\t\tif (result instanceof Error) {\n\t\t\tnext(result)\n\t\t} else {\n\t\t\tres.setHeader(headerName, result)\n\t\t\tnext()\n\t\t}\n\t}\n}\ncontentSecurityPolicy.getDefaultDirectives = getDefaultDirectives\ncontentSecurityPolicy.dangerouslyDisableDefaultSrc = dangerouslyDisableDefaultSrc\n\nconst ALLOWED_POLICIES$2 = new Set([\"require-corp\", \"credentialless\", \"unsafe-none\"])\nfunction getHeaderValueFromOptions$6({policy = \"require-corp\"}) {\n\tif (ALLOWED_POLICIES$2.has(policy)) {\n\t\treturn policy\n\t} else {\n\t\tthrow new Error(`Cross-Origin-Embedder-Policy does not support the ${JSON.stringify(policy)} policy`)\n\t}\n}\nfunction crossOriginEmbedderPolicy(options = {}) {\n\tconst headerValue = getHeaderValueFromOptions$6(options)\n\treturn function crossOriginEmbedderPolicyMiddleware(_req, res, next) {\n\t\tres.setHeader(\"Cross-Origin-Embedder-Policy\", headerValue)\n\t\tnext()\n\t}\n}\n\nconst ALLOWED_POLICIES$1 = new Set([\"same-origin\", \"same-origin-allow-popups\", \"unsafe-none\"])\nfunction getHeaderValueFromOptions$5({policy = \"same-origin\"}) {\n\tif (ALLOWED_POLICIES$1.has(policy)) {\n\t\treturn policy\n\t} else {\n\t\tthrow new Error(`Cross-Origin-Opener-Policy does not support the ${JSON.stringify(policy)} policy`)\n\t}\n}\nfunction crossOriginOpenerPolicy(options = {}) {\n\tconst headerValue = getHeaderValueFromOptions$5(options)\n\treturn function crossOriginOpenerPolicyMiddleware(_req, res, next) {\n\t\tres.setHeader(\"Cross-Origin-Opener-Policy\", headerValue)\n\t\tnext()\n\t}\n}\n\nconst ALLOWED_POLICIES = new Set([\"same-origin\", \"same-site\", \"cross-origin\"])\nfunction getHeaderValueFromOptions$4({policy = \"same-origin\"}) {\n\tif (ALLOWED_POLICIES.has(policy)) {\n\t\treturn policy\n\t} else {\n\t\tthrow new Error(`Cross-Origin-Resource-Policy does not support the ${JSON.stringify(policy)} policy`)\n\t}\n}\nfunction crossOriginResourcePolicy(options = {}) {\n\tconst headerValue = getHeaderValueFromOptions$4(options)\n\treturn function crossOriginResourcePolicyMiddleware(_req, res, next) {\n\t\tres.setHeader(\"Cross-Origin-Resource-Policy\", headerValue)\n\t\tnext()\n\t}\n}\n\nfunction originAgentCluster() {\n\treturn function originAgentClusterMiddleware(_req, res, next) {\n\t\tres.setHeader(\"Origin-Agent-Cluster\", \"?1\")\n\t\tnext()\n\t}\n}\n\nconst ALLOWED_TOKENS = new Set([\"no-referrer\", \"no-referrer-when-downgrade\", \"same-origin\", \"origin\", \"strict-origin\", \"origin-when-cross-origin\", \"strict-origin-when-cross-origin\", \"unsafe-url\", \"\"])\nfunction getHeaderValueFromOptions$3({policy = [\"no-referrer\"]}) {\n\tconst tokens = typeof policy === \"string\" ? [policy] : policy\n\tif (tokens.length === 0) {\n\t\tthrow new Error(\"Referrer-Policy received no policy tokens\")\n\t}\n\tconst tokensSeen = new Set()\n\ttokens.forEach(token => {\n\t\tif (!ALLOWED_TOKENS.has(token)) {\n\t\t\tthrow new Error(`Referrer-Policy received an unexpected policy token ${JSON.stringify(token)}`)\n\t\t} else if (tokensSeen.has(token)) {\n\t\t\tthrow new Error(`Referrer-Policy received a duplicate policy token ${JSON.stringify(token)}`)\n\t\t}\n\t\ttokensSeen.add(token)\n\t})\n\treturn tokens.join(\",\")\n}\nfunction referrerPolicy(options = {}) {\n\tconst headerValue = getHeaderValueFromOptions$3(options)\n\treturn function referrerPolicyMiddleware(_req, res, next) {\n\t\tres.setHeader(\"Referrer-Policy\", headerValue)\n\t\tnext()\n\t}\n}\n\nconst DEFAULT_MAX_AGE = 365 * 24 * 60 * 60\nfunction parseMaxAge(value = DEFAULT_MAX_AGE) {\n\tif (value >= 0 && Number.isFinite(value)) {\n\t\treturn Math.floor(value)\n\t} else {\n\t\tthrow new Error(`Strict-Transport-Security: ${JSON.stringify(value)} is not a valid value for maxAge. Please choose a positive integer.`)\n\t}\n}\nfunction getHeaderValueFromOptions$2(options) {\n\tif (\"maxage\" in options) {\n\t\tthrow new Error(\"Strict-Transport-Security received an unsupported property, `maxage`. Did you mean to pass `maxAge`?\")\n\t}\n\tif (\"includeSubdomains\" in options) {\n\t\tthrow new Error('Strict-Transport-Security middleware should use `includeSubDomains` instead of `includeSubdomains`. (The correct one has an uppercase \"D\".)')\n\t}\n\tconst directives = [`max-age=${parseMaxAge(options.maxAge)}`]\n\tif (options.includeSubDomains === undefined || options.includeSubDomains) {\n\t\tdirectives.push(\"includeSubDomains\")\n\t}\n\tif (options.preload) {\n\t\tdirectives.push(\"preload\")\n\t}\n\treturn directives.join(\"; \")\n}\nfunction strictTransportSecurity(options = {}) {\n\tconst headerValue = getHeaderValueFromOptions$2(options)\n\treturn function strictTransportSecurityMiddleware(_req, res, next) {\n\t\tres.setHeader(\"Strict-Transport-Security\", headerValue)\n\t\tnext()\n\t}\n}\n\nfunction xContentTypeOptions() {\n\treturn function xContentTypeOptionsMiddleware(_req, res, next) {\n\t\tres.setHeader(\"X-Content-Type-Options\", \"nosniff\")\n\t\tnext()\n\t}\n}\n\nfunction xDnsPrefetchControl(options = {}) {\n\tconst headerValue = options.allow ? \"on\" : \"off\"\n\treturn function xDnsPrefetchControlMiddleware(_req, res, next) {\n\t\tres.setHeader(\"X-DNS-Prefetch-Control\", headerValue)\n\t\tnext()\n\t}\n}\n\nfunction xDownloadOptions() {\n\treturn function xDownloadOptionsMiddleware(_req, res, next) {\n\t\tres.setHeader(\"X-Download-Options\", \"noopen\")\n\t\tnext()\n\t}\n}\n\nfunction getHeaderValueFromOptions$1({action = \"sameorigin\"}) {\n\tconst normalizedAction = typeof action === \"string\" ? action.toUpperCase() : action\n\tswitch (normalizedAction) {\n\t\tcase \"SAME-ORIGIN\":\n\t\t\treturn \"SAMEORIGIN\"\n\t\tcase \"DENY\":\n\t\tcase \"SAMEORIGIN\":\n\t\t\treturn normalizedAction\n\t\tdefault:\n\t\t\tthrow new Error(`X-Frame-Options received an invalid action ${JSON.stringify(action)}`)\n\t}\n}\nfunction xFrameOptions(options = {}) {\n\tconst headerValue = getHeaderValueFromOptions$1(options)\n\treturn function xFrameOptionsMiddleware(_req, res, next) {\n\t\tres.setHeader(\"X-Frame-Options\", headerValue)\n\t\tnext()\n\t}\n}\n\nconst ALLOWED_PERMITTED_POLICIES = new Set([\"none\", \"master-only\", \"by-content-type\", \"all\"])\nfunction getHeaderValueFromOptions({permittedPolicies = \"none\"}) {\n\tif (ALLOWED_PERMITTED_POLICIES.has(permittedPolicies)) {\n\t\treturn permittedPolicies\n\t} else {\n\t\tthrow new Error(`X-Permitted-Cross-Domain-Policies does not support ${JSON.stringify(permittedPolicies)}`)\n\t}\n}\nfunction xPermittedCrossDomainPolicies(options = {}) {\n\tconst headerValue = getHeaderValueFromOptions(options)\n\treturn function xPermittedCrossDomainPoliciesMiddleware(_req, res, next) {\n\t\tres.setHeader(\"X-Permitted-Cross-Domain-Policies\", headerValue)\n\t\tnext()\n\t}\n}\n\nfunction xPoweredBy() {\n\treturn function xPoweredByMiddleware(_req, res, next) {\n\t\tres.removeHeader(\"X-Powered-By\")\n\t\tnext()\n\t}\n}\n\nfunction xXssProtection() {\n\treturn function xXssProtectionMiddleware(_req, res, next) {\n\t\tres.setHeader(\"X-XSS-Protection\", \"0\")\n\t\tnext()\n\t}\n}\n\nfunction getMiddlewareFunctionsFromOptions(options) {\n\tconst result = []\n\tswitch (options.contentSecurityPolicy) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(contentSecurityPolicy())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(contentSecurityPolicy(options.contentSecurityPolicy))\n\t\t\tbreak\n\t}\n\tswitch (options.crossOriginEmbedderPolicy) {\n\t\tcase undefined:\n\t\tcase false:\n\t\t\tbreak\n\t\tcase true:\n\t\t\tresult.push(crossOriginEmbedderPolicy())\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(crossOriginEmbedderPolicy(options.crossOriginEmbedderPolicy))\n\t\t\tbreak\n\t}\n\tswitch (options.crossOriginOpenerPolicy) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(crossOriginOpenerPolicy())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(crossOriginOpenerPolicy(options.crossOriginOpenerPolicy))\n\t\t\tbreak\n\t}\n\tswitch (options.crossOriginResourcePolicy) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(crossOriginResourcePolicy())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(crossOriginResourcePolicy(options.crossOriginResourcePolicy))\n\t\t\tbreak\n\t}\n\tswitch (options.originAgentCluster) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(originAgentCluster())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tconsole.warn(\"Origin-Agent-Cluster does not take options. Remove the property to silence this warning.\")\n\t\t\tresult.push(originAgentCluster())\n\t\t\tbreak\n\t}\n\tswitch (options.referrerPolicy) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(referrerPolicy())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(referrerPolicy(options.referrerPolicy))\n\t\t\tbreak\n\t}\n\tif (\"strictTransportSecurity\" in options && \"hsts\" in options) {\n\t\tthrow new Error(\"Strict-Transport-Security option was specified twice. Remove `hsts` to silence this warning.\")\n\t}\n\tconst strictTransportSecurityOption = options.strictTransportSecurity ?? options.hsts\n\tswitch (strictTransportSecurityOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(strictTransportSecurity())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(strictTransportSecurity(strictTransportSecurityOption))\n\t\t\tbreak\n\t}\n\tif (\"xContentTypeOptions\" in options && \"noSniff\" in options) {\n\t\tthrow new Error(\"X-Content-Type-Options option was specified twice. Remove `noSniff` to silence this warning.\")\n\t}\n\tconst xContentTypeOptionsOption = options.xContentTypeOptions ?? options.noSniff\n\tswitch (xContentTypeOptionsOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(xContentTypeOptions())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tconsole.warn(\"X-Content-Type-Options does not take options. Remove the property to silence this warning.\")\n\t\t\tresult.push(xContentTypeOptions())\n\t\t\tbreak\n\t}\n\tif (\"xDnsPrefetchControl\" in options && \"dnsPrefetchControl\" in options) {\n\t\tthrow new Error(\"X-DNS-Prefetch-Control option was specified twice. Remove `dnsPrefetchControl` to silence this warning.\")\n\t}\n\tconst xDnsPrefetchControlOption = options.xDnsPrefetchControl ?? options.dnsPrefetchControl\n\tswitch (xDnsPrefetchControlOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(xDnsPrefetchControl())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(xDnsPrefetchControl(xDnsPrefetchControlOption))\n\t\t\tbreak\n\t}\n\tif (\"xDownloadOptions\" in options && \"ieNoOpen\" in options) {\n\t\tthrow new Error(\"X-Download-Options option was specified twice. Remove `ieNoOpen` to silence this warning.\")\n\t}\n\tconst xDownloadOptionsOption = options.xDownloadOptions ?? options.ieNoOpen\n\tswitch (xDownloadOptionsOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(xDownloadOptions())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tconsole.warn(\"X-Download-Options does not take options. Remove the property to silence this warning.\")\n\t\t\tresult.push(xDownloadOptions())\n\t\t\tbreak\n\t}\n\tif (\"xFrameOptions\" in options && \"frameguard\" in options) {\n\t\tthrow new Error(\"X-Frame-Options option was specified twice. Remove `frameguard` to silence this warning.\")\n\t}\n\tconst xFrameOptionsOption = options.xFrameOptions ?? options.frameguard\n\tswitch (xFrameOptionsOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(xFrameOptions())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(xFrameOptions(xFrameOptionsOption))\n\t\t\tbreak\n\t}\n\tif (\"xPermittedCrossDomainPolicies\" in options && \"permittedCrossDomainPolicies\" in options) {\n\t\tthrow new Error(\"X-Permitted-Cross-Domain-Policies option was specified twice. Remove `permittedCrossDomainPolicies` to silence this warning.\")\n\t}\n\tconst xPermittedCrossDomainPoliciesOption = options.xPermittedCrossDomainPolicies ?? options.permittedCrossDomainPolicies\n\tswitch (xPermittedCrossDomainPoliciesOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(xPermittedCrossDomainPolicies())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tresult.push(xPermittedCrossDomainPolicies(xPermittedCrossDomainPoliciesOption))\n\t\t\tbreak\n\t}\n\tif (\"xPoweredBy\" in options && \"hidePoweredBy\" in options) {\n\t\tthrow new Error(\"X-Powered-By option was specified twice. Remove `hidePoweredBy` to silence this warning.\")\n\t}\n\tconst xPoweredByOption = options.xPoweredBy ?? options.hidePoweredBy\n\tswitch (xPoweredByOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(xPoweredBy())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tconsole.warn(\"X-Powered-By does not take options. Remove the property to silence this warning.\")\n\t\t\tresult.push(xPoweredBy())\n\t\t\tbreak\n\t}\n\tif (\"xXssProtection\" in options && \"xssFilter\" in options) {\n\t\tthrow new Error(\"X-XSS-Protection option was specified twice. Remove `xssFilter` to silence this warning.\")\n\t}\n\tconst xXssProtectionOption = options.xXssProtection ?? options.xssFilter\n\tswitch (xXssProtectionOption) {\n\t\tcase undefined:\n\t\tcase true:\n\t\t\tresult.push(xXssProtection())\n\t\t\tbreak\n\t\tcase false:\n\t\t\tbreak\n\t\tdefault:\n\t\t\tconsole.warn(\"X-XSS-Protection does not take options. Remove the property to silence this warning.\")\n\t\t\tresult.push(xXssProtection())\n\t\t\tbreak\n\t}\n\treturn result\n}\nconst helmet = Object.assign(\n\tfunction helmet(options = {}) {\n\t\t// People should be able to pass an options object with no prototype,\n\t\t// so we want this optional chaining.\n\t\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n\t\tif (options.constructor?.name === \"IncomingMessage\") {\n\t\t\tthrow new Error(\"It appears you have done something like `app.use(helmet)`, but it should be `app.use(helmet())`.\")\n\t\t}\n\t\tconst middlewareFunctions = getMiddlewareFunctionsFromOptions(options)\n\t\treturn function helmetMiddleware(req, res, next) {\n\t\t\tlet middlewareIndex = 0\n\t\t\t;(function internalNext(err) {\n\t\t\t\tif (err) {\n\t\t\t\t\tnext(err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst middlewareFunction = middlewareFunctions[middlewareIndex]\n\t\t\t\tif (middlewareFunction) {\n\t\t\t\t\tmiddlewareIndex++\n\t\t\t\t\tmiddlewareFunction(req, res, internalNext)\n\t\t\t\t} else {\n\t\t\t\t\tnext()\n\t\t\t\t}\n\t\t\t})()\n\t\t}\n\t},\n\t{\n\t\tcontentSecurityPolicy,\n\t\tcrossOriginEmbedderPolicy,\n\t\tcrossOriginOpenerPolicy,\n\t\tcrossOriginResourcePolicy,\n\t\toriginAgentCluster,\n\t\treferrerPolicy,\n\t\tstrictTransportSecurity,\n\t\txContentTypeOptions,\n\t\txDnsPrefetchControl,\n\t\txDownloadOptions,\n\t\txFrameOptions,\n\t\txPermittedCrossDomainPolicies,\n\t\txPoweredBy,\n\t\txXssProtection,\n\t\t// Legacy aliases\n\t\tdnsPrefetchControl: xDnsPrefetchControl,\n\t\txssFilter: xXssProtection,\n\t\tpermittedCrossDomainPolicies: xPermittedCrossDomainPolicies,\n\t\tieNoOpen: xDownloadOptions,\n\t\tnoSniff: xContentTypeOptions,\n\t\tframeguard: xFrameOptions,\n\t\thidePoweredBy: xPoweredBy,\n\t\thsts: strictTransportSecurity\n\t}\n)\n\nexport {contentSecurityPolicy, crossOriginEmbedderPolicy, crossOriginOpenerPolicy, crossOriginResourcePolicy, helmet as default, xDnsPrefetchControl as dnsPrefetchControl, xFrameOptions as frameguard, xPoweredBy as hidePoweredBy, strictTransportSecurity as hsts, xDownloadOptions as ieNoOpen, xContentTypeOptions as noSniff, originAgentCluster, xPermittedCrossDomainPolicies as permittedCrossDomainPolicies, referrerPolicy, strictTransportSecurity, xContentTypeOptions, xDnsPrefetchControl, xDownloadOptions, xFrameOptions, xPermittedCrossDomainPolicies, xPoweredBy, xXssProtection, xXssProtection as xssFilter}\n", "// source/ip-key-generator.ts\nimport { isIPv6 } from \"node:net\";\nimport { Address6 } from \"ip-address\";\nfunction ipKeyGenerator(ip, ipv6Subnet = 56) {\n if (ipv6Subnet && isIPv6(ip)) {\n return `${new Address6(`${ip}/${ipv6Subnet}`).startAddress().correctForm()}/${ipv6Subnet}`;\n }\n return ip;\n}\n\n// source/memory-store.ts\nvar MemoryStore = class {\n constructor(validations2) {\n this.validations = validations2;\n /**\n * These two maps store usage (requests) and reset time by key (for example, IP\n * addresses or API keys).\n *\n * They are split into two to avoid having to iterate through the entire set to\n * determine which ones need reset. Instead, `Client`s are moved from `previous`\n * to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients\n * left in `previous`, i.e., those that have not made any recent requests, are\n * known to be expired and can be deleted in bulk.\n */\n this.previous = /* @__PURE__ */ new Map();\n this.current = /* @__PURE__ */ new Map();\n /**\n * Confirmation that the keys incremented in once instance of MemoryStore\n * cannot affect other instances.\n */\n this.localKeys = true;\n }\n /**\n * Method that initializes the store.\n *\n * @param options {Options} - The options used to setup the middleware.\n */\n init(options) {\n this.windowMs = options.windowMs;\n this.validations?.windowMs(this.windowMs);\n if (this.interval) clearInterval(this.interval);\n this.interval = setInterval(() => {\n this.clearExpired();\n }, this.windowMs);\n this.interval.unref?.();\n }\n /**\n * Method to fetch a client's hit count and reset time.\n *\n * @param key {string} - The identifier for a client.\n *\n * @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.\n *\n * @public\n */\n async get(key) {\n return this.current.get(key) ?? this.previous.get(key);\n }\n /**\n * Method to increment a client's hit counter.\n *\n * @param key {string} - The identifier for a client.\n *\n * @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.\n *\n * @public\n */\n async increment(key) {\n const client = this.getClient(key);\n const now = Date.now();\n if (client.resetTime.getTime() <= now) {\n this.resetClient(client, now);\n }\n client.totalHits++;\n return client;\n }\n /**\n * Method to decrement a client's hit counter.\n *\n * @param key {string} - The identifier for a client.\n *\n * @public\n */\n async decrement(key) {\n const client = this.getClient(key);\n if (client.totalHits > 0) client.totalHits--;\n }\n /**\n * Method to reset a client's hit counter.\n *\n * @param key {string} - The identifier for a client.\n *\n * @public\n */\n async resetKey(key) {\n this.current.delete(key);\n this.previous.delete(key);\n }\n /**\n * Method to reset everyone's hit counter.\n *\n * @public\n */\n async resetAll() {\n this.current.clear();\n this.previous.clear();\n }\n /**\n * Method to stop the timer (if currently running) and prevent any memory\n * leaks.\n *\n * @public\n */\n shutdown() {\n clearInterval(this.interval);\n void this.resetAll();\n }\n /**\n * Recycles a client by setting its hit count to zero, and reset time to\n * `windowMs` milliseconds from now.\n *\n * NOT to be confused with `#resetKey()`, which removes a client from both the\n * `current` and `previous` maps.\n *\n * @param client {Client} - The client to recycle.\n * @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.\n *\n * @return {Client} - The modified client that was passed in, to allow for chaining.\n */\n resetClient(client, now = Date.now()) {\n client.totalHits = 0;\n client.resetTime.setTime(now + this.windowMs);\n return client;\n }\n /**\n * Retrieves or creates a client, given a key. Also ensures that the client being\n * returned is in the `current` map.\n *\n * @param key {string} - The key under which the client is (or is to be) stored.\n *\n * @returns {Client} - The requested client.\n */\n getClient(key) {\n if (this.current.has(key)) return this.current.get(key);\n let client;\n if (this.previous.has(key)) {\n client = this.previous.get(key);\n this.previous.delete(key);\n } else {\n client = { totalHits: 0, resetTime: /* @__PURE__ */ new Date() };\n this.resetClient(client);\n }\n this.current.set(key, client);\n return client;\n }\n /**\n * Move current clients to previous, create a new map for current.\n *\n * This function is called every `windowMs`.\n */\n clearExpired() {\n this.previous = this.current;\n this.current = /* @__PURE__ */ new Map();\n }\n};\n\n// source/rate-limit.ts\nimport { isIPv6 as isIPv62 } from \"node:net\";\n\n// source/headers.ts\nimport { Buffer } from \"node:buffer\";\nimport { createHash } from \"node:crypto\";\nvar SUPPORTED_DRAFT_VERSIONS = [\n \"draft-6\",\n \"draft-7\",\n \"draft-8\"\n];\nvar getResetSeconds = (windowMs, resetTime) => {\n let resetSeconds;\n if (resetTime) {\n const deltaSeconds = Math.ceil((resetTime.getTime() - Date.now()) / 1e3);\n resetSeconds = Math.max(0, deltaSeconds);\n } else {\n resetSeconds = Math.ceil(windowMs / 1e3);\n }\n return resetSeconds;\n};\nvar getPartitionKey = (key) => {\n const hash = createHash(\"sha256\");\n hash.update(key);\n const partitionKey = hash.digest(\"hex\").slice(0, 12);\n return Buffer.from(partitionKey).toString(\"base64\");\n};\nvar setLegacyHeaders = (response, info) => {\n if (response.headersSent) return;\n response.setHeader(\"X-RateLimit-Limit\", info.limit.toString());\n response.setHeader(\"X-RateLimit-Remaining\", info.remaining.toString());\n if (info.resetTime instanceof Date) {\n response.setHeader(\"Date\", (/* @__PURE__ */ new Date()).toUTCString());\n response.setHeader(\n \"X-RateLimit-Reset\",\n Math.ceil(info.resetTime.getTime() / 1e3).toString()\n );\n }\n};\nvar setDraft6Headers = (response, info, windowMs) => {\n if (response.headersSent) return;\n const windowSeconds = Math.ceil(windowMs / 1e3);\n const resetSeconds = getResetSeconds(windowMs, info.resetTime);\n response.setHeader(\"RateLimit-Policy\", `${info.limit};w=${windowSeconds}`);\n response.setHeader(\"RateLimit-Limit\", info.limit.toString());\n response.setHeader(\"RateLimit-Remaining\", info.remaining.toString());\n if (typeof resetSeconds === \"number\")\n response.setHeader(\"RateLimit-Reset\", resetSeconds.toString());\n};\nvar setDraft7Headers = (response, info, windowMs) => {\n if (response.headersSent) return;\n const windowSeconds = Math.ceil(windowMs / 1e3);\n const resetSeconds = getResetSeconds(windowMs, info.resetTime);\n response.setHeader(\"RateLimit-Policy\", `${info.limit};w=${windowSeconds}`);\n response.setHeader(\n \"RateLimit\",\n `limit=${info.limit}, remaining=${info.remaining}, reset=${resetSeconds}`\n );\n};\nvar setDraft8Headers = (response, info, windowMs, name, key) => {\n if (response.headersSent) return;\n const windowSeconds = Math.ceil(windowMs / 1e3);\n const resetSeconds = getResetSeconds(windowMs, info.resetTime);\n const partitionKey = getPartitionKey(key);\n const header = `r=${info.remaining}; t=${resetSeconds}`;\n const policy = `q=${info.limit}; w=${windowSeconds}; pk=:${partitionKey}:`;\n response.append(\"RateLimit\", `\"${name}\"; ${header}`);\n response.append(\"RateLimit-Policy\", `\"${name}\"; ${policy}`);\n};\nvar setRetryAfterHeader = (response, info, windowMs) => {\n if (response.headersSent) return;\n const resetSeconds = getResetSeconds(windowMs, info.resetTime);\n response.setHeader(\"Retry-After\", resetSeconds.toString());\n};\n\n// source/utils.ts\nvar omitUndefinedProperties = (passedOptions) => {\n const omittedOptions = {};\n for (const k of Object.keys(passedOptions)) {\n const key = k;\n if (passedOptions[key] !== void 0) {\n omittedOptions[key] = passedOptions[key];\n }\n }\n return omittedOptions;\n};\n\n// source/validations.ts\nimport { isIP } from \"node:net\";\nvar ValidationError = class extends Error {\n /**\n * The code must be a string, in snake case and all capital, that starts with\n * the substring `ERR_ERL_`.\n *\n * The message must be a string, starting with an uppercase character,\n * describing the issue in detail.\n */\n constructor(code, message) {\n const url = `https://express-rate-limit.github.io/${code}/`;\n super(`${message} See ${url} for more information.`);\n this.name = this.constructor.name;\n this.code = code;\n this.help = url;\n }\n};\nvar ChangeWarning = class extends ValidationError {\n};\nvar usedStores = /* @__PURE__ */ new Set();\nvar singleCountKeys = /* @__PURE__ */ new WeakMap();\nvar validations = {\n enabled: {\n default: true\n },\n // Should be EnabledValidations type, but that's a circular reference\n disable() {\n for (const k of Object.keys(this.enabled)) this.enabled[k] = false;\n },\n /**\n * Checks whether the IP address is valid, and that it does not have a port\n * number in it.\n *\n * See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_invalid_ip_address.\n *\n * @param ip {string | undefined} - The IP address provided by Express as request.ip.\n *\n * @returns {void}\n */\n ip(ip) {\n if (ip === void 0) {\n throw new ValidationError(\n \"ERR_ERL_UNDEFINED_IP_ADDRESS\",\n `An undefined 'request.ip' was detected. This might indicate a misconfiguration or the connection being destroyed prematurely.`\n );\n }\n if (!isIP(ip)) {\n throw new ValidationError(\n \"ERR_ERL_INVALID_IP_ADDRESS\",\n `An invalid 'request.ip' (${ip}) was detected. Consider passing a custom 'keyGenerator' function to the rate limiter.`\n );\n }\n },\n /**\n * Makes sure the trust proxy setting is not set to `true`.\n *\n * See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_permissive_trust_proxy.\n *\n * @param request {Request} - The Express request object.\n *\n * @returns {void}\n */\n trustProxy(request) {\n if (request.app.get(\"trust proxy\") === true) {\n throw new ValidationError(\n \"ERR_ERL_PERMISSIVE_TRUST_PROXY\",\n `The Express 'trust proxy' setting is true, which allows anyone to trivially bypass IP-based rate limiting.`\n );\n }\n },\n /**\n * Makes sure the trust proxy setting is set in case the `X-Forwarded-For`\n * header is present.\n *\n * See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_unset_trust_proxy.\n *\n * @param request {Request} - The Express request object.\n *\n * @returns {void}\n */\n xForwardedForHeader(request) {\n if (request.headers[\"x-forwarded-for\"] && request.app.get(\"trust proxy\") === false) {\n throw new ValidationError(\n \"ERR_ERL_UNEXPECTED_X_FORWARDED_FOR\",\n `The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default). This could indicate a misconfiguration which would prevent express-rate-limit from accurately identifying users.`\n );\n }\n },\n /**\n * Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)\n *\n * @param request {Request} - The Express request object.\n *\n * @returns {void}\n */\n forwardedHeader(request) {\n if (request.headers.forwarded && request.ip === request.socket?.remoteAddress) {\n throw new ValidationError(\n \"ERR_ERL_FORWARDED_HEADER\",\n `The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.`\n );\n }\n },\n /**\n * Ensures totalHits value from store is a positive integer.\n *\n * @param hits {any} - The `totalHits` returned by the store.\n */\n positiveHits(hits) {\n if (typeof hits !== \"number\" || hits < 1 || hits !== Math.round(hits)) {\n throw new ValidationError(\n \"ERR_ERL_INVALID_HITS\",\n `The totalHits value returned from the store must be a positive integer, got ${hits}`\n );\n }\n },\n /**\n * Ensures a single store instance is not used with multiple express-rate-limit instances\n */\n unsharedStore(store) {\n if (usedStores.has(store)) {\n const maybeUniquePrefix = store?.localKeys ? \"\" : \" (with a unique prefix)\";\n throw new ValidationError(\n \"ERR_ERL_STORE_REUSE\",\n `A Store instance must not be shared across multiple rate limiters. Create a new instance of ${store.constructor.name}${maybeUniquePrefix} for each limiter instead.`\n );\n }\n usedStores.add(store);\n },\n /**\n * Ensures a given key is incremented only once per request.\n *\n * @param request {Request} - The Express request object.\n * @param store {Store} - The store class.\n * @param key {string} - The key used to store the client's hit count.\n *\n * @returns {void}\n */\n singleCount(request, store, key) {\n let storeKeys = singleCountKeys.get(request);\n if (!storeKeys) {\n storeKeys = /* @__PURE__ */ new Map();\n singleCountKeys.set(request, storeKeys);\n }\n const storeKey = store.localKeys ? store : store.constructor.name;\n let keys = storeKeys.get(storeKey);\n if (!keys) {\n keys = [];\n storeKeys.set(storeKey, keys);\n }\n const prefixedKey = `${store.prefix ?? \"\"}${key}`;\n if (keys.includes(prefixedKey)) {\n throw new ValidationError(\n \"ERR_ERL_DOUBLE_COUNT\",\n `The hit count for ${key} was incremented more than once for a single request.`\n );\n }\n keys.push(prefixedKey);\n },\n /**\n * Warns the user that the behaviour for `max: 0` / `limit: 0` is\n * changing in the next major release.\n *\n * @param limit {number} - The maximum number of hits per client.\n *\n * @returns {void}\n */\n limit(limit) {\n if (limit === 0) {\n throw new ChangeWarning(\n \"WRN_ERL_MAX_ZERO\",\n \"Setting limit or max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7\"\n );\n }\n },\n /**\n * Warns the user that the `draft_polli_ratelimit_headers` option is deprecated\n * and will be removed in the next major release.\n *\n * @param draft_polli_ratelimit_headers {any | undefined} - The now-deprecated setting that was used to enable standard headers.\n *\n * @returns {void}\n */\n draftPolliHeaders(draft_polli_ratelimit_headers) {\n if (draft_polli_ratelimit_headers) {\n throw new ChangeWarning(\n \"WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS\",\n `The draft_polli_ratelimit_headers configuration option is deprecated and has been removed in express-rate-limit v7, please set standardHeaders: 'draft-6' instead.`\n );\n }\n },\n /**\n * Warns the user that the `onLimitReached` option is deprecated and\n * will be removed in the next major release.\n *\n * @param onLimitReached {any | undefined} - The maximum number of hits per client.\n *\n * @returns {void}\n */\n onLimitReached(onLimitReached) {\n if (onLimitReached) {\n throw new ChangeWarning(\n \"WRN_ERL_DEPRECATED_ON_LIMIT_REACHED\",\n \"The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7.\"\n );\n }\n },\n /**\n * Warns the user when an invalid/unsupported version of the draft spec is passed.\n *\n * @param version {any | undefined} - The version passed by the user.\n *\n * @returns {void}\n */\n headersDraftVersion(version) {\n if (typeof version !== \"string\" || // @ts-expect-error This is fine. If version is not in the array, it will just return false.\n !SUPPORTED_DRAFT_VERSIONS.includes(version)) {\n const versionString = SUPPORTED_DRAFT_VERSIONS.join(\", \");\n throw new ValidationError(\n \"ERR_ERL_HEADERS_UNSUPPORTED_DRAFT_VERSION\",\n `standardHeaders: only the following versions of the IETF draft specification are supported: ${versionString}.`\n );\n }\n },\n /**\n * Warns the user when the selected headers option requires a reset time but\n * the store does not provide one.\n *\n * @param resetTime {Date | undefined} - The timestamp when the client's hit count will be reset.\n *\n * @returns {void}\n */\n headersResetTime(resetTime) {\n if (!resetTime) {\n throw new ValidationError(\n \"ERR_ERL_HEADERS_NO_RESET\",\n `standardHeaders: 'draft-7' requires a 'resetTime', but the store did not provide one. The 'windowMs' value will be used instead, which may cause clients to wait longer than necessary.`\n );\n }\n },\n knownOptions(passedOptions) {\n if (!passedOptions) return;\n const optionsMap = {\n windowMs: true,\n limit: true,\n message: true,\n statusCode: true,\n legacyHeaders: true,\n standardHeaders: true,\n identifier: true,\n requestPropertyName: true,\n skipFailedRequests: true,\n skipSuccessfulRequests: true,\n keyGenerator: true,\n ipv6Subnet: true,\n handler: true,\n skip: true,\n requestWasSuccessful: true,\n store: true,\n validate: true,\n headers: true,\n max: true,\n passOnStoreError: true\n };\n const validOptions = Object.keys(optionsMap).concat(\n \"draft_polli_ratelimit_headers\",\n // not a valid option anymore, but we have a more specific check for this one, so don't warn for it here\n // from express-slow-down - https://github.com/express-rate-limit/express-slow-down/blob/main/source/types.ts#L65\n \"delayAfter\",\n \"delayMs\",\n \"maxDelayMs\"\n );\n for (const key of Object.keys(passedOptions)) {\n if (!validOptions.includes(key)) {\n throw new ValidationError(\n \"ERR_ERL_UNKNOWN_OPTION\",\n `Unexpected configuration option: ${key}`\n // todo: suggest a valid option with a short levenstein distance?\n );\n }\n }\n },\n /**\n * Checks the options.validate setting to ensure that only recognized\n * validations are enabled or disabled.\n *\n * If any unrecognized values are found, an error is logged that\n * includes the list of supported validations.\n */\n validationsConfig() {\n const supportedValidations = Object.keys(this).filter(\n (k) => ![\"enabled\", \"disable\"].includes(k)\n );\n supportedValidations.push(\"default\");\n for (const key of Object.keys(this.enabled)) {\n if (!supportedValidations.includes(key)) {\n throw new ValidationError(\n \"ERR_ERL_UNKNOWN_VALIDATION\",\n `options.validate.${key} is not recognized. Supported validate options are: ${supportedValidations.join(\n \", \"\n )}.`\n );\n }\n }\n },\n /**\n * Checks to see if the instance was created inside of a request handler,\n * which would prevent it from working correctly, with the default memory\n * store (or any other store with localKeys.)\n */\n creationStack(store) {\n const { stack } = new Error(\n \"express-rate-limit validation check (set options.validate.creationStack=false to disable)\"\n );\n if (stack?.includes(\"Layer.handle [as handle_request]\") || // express v4\n stack?.includes(\"Layer.handleRequest\")) {\n if (!store.localKeys) {\n throw new ValidationError(\n \"ERR_ERL_CREATED_IN_REQUEST_HANDLER\",\n \"express-rate-limit instance should *usually* be created at app initialization, not when responding to a request.\"\n );\n }\n throw new ValidationError(\n \"ERR_ERL_CREATED_IN_REQUEST_HANDLER\",\n \"express-rate-limit instance should be created at app initialization, not when responding to a request.\"\n );\n }\n },\n ipv6Subnet(ipv6Subnet) {\n if (ipv6Subnet === false) {\n return;\n }\n if (!Number.isInteger(ipv6Subnet) || ipv6Subnet < 32 || ipv6Subnet > 64) {\n throw new ValidationError(\n \"ERR_ERL_IPV6_SUBNET\",\n `Unexpected ipv6Subnet value: ${ipv6Subnet}. Expected an integer between 32 and 64 (usually 48-64).`\n );\n }\n },\n ipv6SubnetOrKeyGenerator(options) {\n if (options.ipv6Subnet !== void 0 && options.keyGenerator) {\n throw new ValidationError(\n \"ERR_ERL_IPV6SUBNET_OR_KEYGENERATOR\",\n `Incompatible options: the 'ipv6Subnet' option is ignored when a custom 'keyGenerator' function is also set.`\n );\n }\n },\n keyGeneratorIpFallback(keyGenerator) {\n if (!keyGenerator) {\n return;\n }\n const src = keyGenerator.toString();\n if ((src.includes(\"req.ip\") || src.includes(\"request.ip\")) && !src.includes(\"ipKeyGenerator\")) {\n throw new ValidationError(\n \"ERR_ERL_KEY_GEN_IPV6\",\n \"Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits.\"\n );\n }\n },\n /**\n * Checks to see if the window duration is greater than 2^32 - 1. This is only\n * called by the default MemoryStore, since it uses Node's setInterval method.\n *\n * See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.\n */\n windowMs(windowMs) {\n const SET_TIMEOUT_MAX = 2 ** 31 - 1;\n if (typeof windowMs !== \"number\" || Number.isNaN(windowMs) || windowMs < 1 || windowMs > SET_TIMEOUT_MAX) {\n throw new ValidationError(\n \"ERR_ERL_WINDOW_MS\",\n `Invalid windowMs value: ${windowMs}${typeof windowMs !== \"number\" ? ` (${typeof windowMs})` : \"\"}, must be a number between 1 and ${SET_TIMEOUT_MAX} when using the default MemoryStore`\n );\n }\n }\n};\nvar getValidations = (_enabled) => {\n let enabled;\n if (typeof _enabled === \"boolean\") {\n enabled = {\n default: _enabled\n };\n } else {\n enabled = {\n default: true,\n ..._enabled\n };\n }\n const wrappedValidations = { enabled };\n for (const [name, validation] of Object.entries(validations)) {\n if (typeof validation === \"function\")\n wrappedValidations[name] = (...args) => {\n if (!(enabled[name] ?? enabled.default)) {\n return;\n }\n try {\n ;\n validation.apply(\n wrappedValidations,\n args\n );\n } catch (error) {\n if (error instanceof ChangeWarning) console.warn(error);\n else console.error(error);\n }\n };\n }\n return wrappedValidations;\n};\n\n// source/rate-limit.ts\nvar isLegacyStore = (store) => (\n // Check that `incr` exists but `increment` does not - store authors might want\n // to keep both around for backwards compatibility.\n typeof store.incr === \"function\" && typeof store.increment !== \"function\"\n);\nvar promisifyStore = (passedStore) => {\n if (!isLegacyStore(passedStore)) {\n return passedStore;\n }\n const legacyStore = passedStore;\n class PromisifiedStore {\n async increment(key) {\n return new Promise((resolve, reject) => {\n legacyStore.incr(\n key,\n (error, totalHits, resetTime) => {\n if (error) reject(error);\n resolve({ totalHits, resetTime });\n }\n );\n });\n }\n async decrement(key) {\n return legacyStore.decrement(key);\n }\n async resetKey(key) {\n return legacyStore.resetKey(key);\n }\n /* istanbul ignore next */\n async resetAll() {\n if (typeof legacyStore.resetAll === \"function\")\n return legacyStore.resetAll();\n }\n }\n return new PromisifiedStore();\n};\nvar getOptionsFromConfig = (config) => {\n const { validations: validations2, ...directlyPassableEntries } = config;\n return {\n ...directlyPassableEntries,\n validate: validations2.enabled\n };\n};\nvar parseOptions = (passedOptions) => {\n const notUndefinedOptions = omitUndefinedProperties(passedOptions);\n const validations2 = getValidations(notUndefinedOptions?.validate ?? true);\n validations2.validationsConfig();\n validations2.knownOptions(passedOptions);\n validations2.draftPolliHeaders(\n // @ts-expect-error see the note above.\n notUndefinedOptions.draft_polli_ratelimit_headers\n );\n validations2.onLimitReached(notUndefinedOptions.onLimitReached);\n if (notUndefinedOptions.ipv6Subnet !== void 0 && typeof notUndefinedOptions.ipv6Subnet !== \"function\") {\n validations2.ipv6Subnet(notUndefinedOptions.ipv6Subnet);\n }\n validations2.keyGeneratorIpFallback(notUndefinedOptions.keyGenerator);\n validations2.ipv6SubnetOrKeyGenerator(notUndefinedOptions);\n let standardHeaders = notUndefinedOptions.standardHeaders ?? false;\n if (standardHeaders === true) standardHeaders = \"draft-6\";\n const config = {\n windowMs: 60 * 1e3,\n limit: passedOptions.max ?? 5,\n // `max` is deprecated, but support it anyways.\n message: \"Too many requests, please try again later.\",\n statusCode: 429,\n legacyHeaders: passedOptions.headers ?? true,\n identifier(request, _response) {\n let duration = \"\";\n const property = config.requestPropertyName;\n const { limit } = request[property];\n const seconds = config.windowMs / 1e3;\n const minutes = config.windowMs / (1e3 * 60);\n const hours = config.windowMs / (1e3 * 60 * 60);\n const days = config.windowMs / (1e3 * 60 * 60 * 24);\n if (seconds < 60) duration = `${seconds}sec`;\n else if (minutes < 60) duration = `${minutes}min`;\n else if (hours < 24) duration = `${hours}hr${hours > 1 ? \"s\" : \"\"}`;\n else duration = `${days}day${days > 1 ? \"s\" : \"\"}`;\n return `${limit}-in-${duration}`;\n },\n requestPropertyName: \"rateLimit\",\n skipFailedRequests: false,\n skipSuccessfulRequests: false,\n requestWasSuccessful: (_request, response) => response.statusCode < 400,\n skip: (_request, _response) => false,\n async keyGenerator(request, response) {\n validations2.ip(request.ip);\n validations2.trustProxy(request);\n validations2.xForwardedForHeader(request);\n validations2.forwardedHeader(request);\n const ip = request.ip;\n let subnet = 56;\n if (isIPv62(ip)) {\n subnet = typeof config.ipv6Subnet === \"function\" ? await config.ipv6Subnet(request, response) : config.ipv6Subnet;\n if (typeof config.ipv6Subnet === \"function\")\n validations2.ipv6Subnet(subnet);\n }\n return ipKeyGenerator(ip, subnet);\n },\n ipv6Subnet: 56,\n async handler(request, response, _next, _optionsUsed) {\n response.status(config.statusCode);\n const message = typeof config.message === \"function\" ? await config.message(\n request,\n response\n ) : config.message;\n if (!response.writableEnded) response.send(message);\n },\n passOnStoreError: false,\n // Allow the default options to be overridden by the passed options.\n ...notUndefinedOptions,\n // `standardHeaders` is resolved into a draft version above, use that.\n standardHeaders,\n // Note that this field is declared after the user's options are spread in,\n // so that this field doesn't get overridden with an un-promisified store!\n store: promisifyStore(\n notUndefinedOptions.store ?? new MemoryStore(validations2)\n ),\n // Print an error to the console if a few known misconfigurations are detected.\n validations: validations2\n };\n if (typeof config.store.increment !== \"function\" || typeof config.store.decrement !== \"function\" || typeof config.store.resetKey !== \"function\" || config.store.resetAll !== void 0 && typeof config.store.resetAll !== \"function\" || config.store.init !== void 0 && typeof config.store.init !== \"function\") {\n throw new TypeError(\n \"An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface.\"\n );\n }\n return config;\n};\nvar handleAsyncErrors = (fn) => async (request, response, next) => {\n try {\n await Promise.resolve(fn(request, response, next)).catch(next);\n } catch (error) {\n next(error);\n }\n};\nvar rateLimit = (passedOptions) => {\n const config = parseOptions(passedOptions ?? {});\n const options = getOptionsFromConfig(config);\n config.validations.creationStack(config.store);\n config.validations.unsharedStore(config.store);\n if (typeof config.store.init === \"function\") config.store.init(options);\n const middleware = handleAsyncErrors(\n async (request, response, next) => {\n const skip = await config.skip(request, response);\n if (skip) {\n next();\n return;\n }\n const augmentedRequest = request;\n const key = await config.keyGenerator(request, response);\n let totalHits = 0;\n let resetTime;\n try {\n const incrementResult = await config.store.increment(key);\n totalHits = incrementResult.totalHits;\n resetTime = incrementResult.resetTime;\n } catch (error) {\n if (config.passOnStoreError) {\n console.error(\n \"express-rate-limit: error from store, allowing request without rate-limiting.\",\n error\n );\n next();\n return;\n }\n throw error;\n }\n config.validations.positiveHits(totalHits);\n config.validations.singleCount(request, config.store, key);\n const retrieveLimit = typeof config.limit === \"function\" ? config.limit(request, response) : config.limit;\n const limit = await retrieveLimit;\n config.validations.limit(limit);\n const info = {\n limit,\n used: totalHits,\n remaining: Math.max(limit - totalHits, 0),\n resetTime,\n key\n };\n Object.defineProperty(info, \"current\", {\n configurable: false,\n enumerable: false,\n value: totalHits\n });\n augmentedRequest[config.requestPropertyName] = info;\n if (config.legacyHeaders && !response.headersSent) {\n setLegacyHeaders(response, info);\n }\n if (config.standardHeaders && !response.headersSent) {\n switch (config.standardHeaders) {\n case \"draft-6\": {\n setDraft6Headers(response, info, config.windowMs);\n break;\n }\n case \"draft-7\": {\n config.validations.headersResetTime(info.resetTime);\n setDraft7Headers(response, info, config.windowMs);\n break;\n }\n case \"draft-8\": {\n const retrieveName = typeof config.identifier === \"function\" ? config.identifier(request, response) : config.identifier;\n const name = await retrieveName;\n config.validations.headersResetTime(info.resetTime);\n setDraft8Headers(response, info, config.windowMs, name, key);\n break;\n }\n default: {\n config.validations.headersDraftVersion(config.standardHeaders);\n break;\n }\n }\n }\n if (config.skipFailedRequests || config.skipSuccessfulRequests) {\n let decremented = false;\n const decrementKey = async () => {\n if (!decremented) {\n await config.store.decrement(key);\n decremented = true;\n }\n };\n if (config.skipFailedRequests) {\n response.on(\"finish\", async () => {\n if (!await config.requestWasSuccessful(request, response))\n await decrementKey();\n });\n response.on(\"close\", async () => {\n if (!response.writableEnded) await decrementKey();\n });\n response.on(\"error\", async () => {\n await decrementKey();\n });\n }\n if (config.skipSuccessfulRequests) {\n response.on(\"finish\", async () => {\n if (await config.requestWasSuccessful(request, response))\n await decrementKey();\n });\n }\n }\n config.validations.disable();\n if (totalHits > limit) {\n if (config.legacyHeaders || config.standardHeaders) {\n setRetryAfterHeader(response, info, config.windowMs);\n }\n config.handler(request, response, next, options);\n return;\n }\n next();\n }\n );\n const getThrowFn = () => {\n throw new Error(\"The current store does not support the get/getKey method\");\n };\n middleware.resetKey = config.store.resetKey.bind(config.store);\n middleware.getKey = typeof config.store.get === \"function\" ? config.store.get.bind(config.store) : getThrowFn;\n return middleware;\n};\nvar rate_limit_default = rateLimit;\nexport {\n MemoryStore,\n rate_limit_default as default,\n ipKeyGenerator,\n rate_limit_default as rateLimit\n};\n", "/**\n * Shim bun:sqlite \u2192 better-sqlite3\n *\n * Provides a bun:sqlite-compatible API using better-sqlite3\n * to allow execution on plain Node.js.\n */\n\nimport BetterSqlite3 from 'better-sqlite3';\n\n/**\n * bun:sqlite-compatible Database class\n */\nexport class Database {\n private _db: BetterSqlite3.Database;\n private _stmtCache: Map<string, BunQueryCompat> = new Map();\n\n constructor(path: string, options?: { create?: boolean; readwrite?: boolean }) {\n this._db = new BetterSqlite3(path, {\n // better-sqlite3 creates the file by default ('create' not needed)\n readonly: options?.readwrite === false ? true : false\n });\n }\n\n /**\n * Execute a SQL query without results\n */\n run(sql: string, params?: any[]): { lastInsertRowid: number | bigint; changes: number } {\n const stmt = this._db.prepare(sql);\n const result = params ? stmt.run(...params) : stmt.run();\n return result;\n }\n\n /**\n * Prepare a query with bun:sqlite-compatible interface.\n * Returns a cached prepared statement for repeated queries.\n */\n query(sql: string): BunQueryCompat {\n let cached = this._stmtCache.get(sql);\n if (!cached) {\n cached = new BunQueryCompat(this._db, sql);\n this._stmtCache.set(sql, cached);\n }\n return cached;\n }\n\n /**\n * Create a transaction\n */\n transaction<T>(fn: (...args: any[]) => T): (...args: any[]) => T {\n return this._db.transaction(fn) as any;\n }\n\n /**\n * Close the connection\n */\n close(): void {\n this._stmtCache.clear();\n this._db.close();\n }\n}\n\n/**\n * Query wrapper compatible with the bun:sqlite Statement API.\n * Prepares the statement once at construction time (cached).\n */\nclass BunQueryCompat {\n private _stmt: BetterSqlite3.Statement;\n\n constructor(db: BetterSqlite3.Database, sql: string) {\n this._stmt = db.prepare(sql);\n }\n\n /**\n * Returns all rows\n */\n all(...params: any[]): any[] {\n return params.length > 0 ? this._stmt.all(...params) : this._stmt.all();\n }\n\n /**\n * Returns the first row or null\n */\n get(...params: any[]): any {\n return params.length > 0 ? this._stmt.get(...params) : this._stmt.get();\n }\n\n /**\n * Execute without results\n */\n run(...params: any[]): { lastInsertRowid: number | bigint; changes: number } {\n return params.length > 0 ? this._stmt.run(...params) : this._stmt.run();\n }\n}\n\nexport default { Database };\n", "import { join, dirname, basename } from 'path';\nimport { homedir } from 'os';\nimport { existsSync, mkdirSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { fileURLToPath } from 'url';\nimport { logger } from '../utils/logger.js';\n\n// Get __dirname that works in both ESM and CJS contexts\nfunction getDirname(): string {\n if (typeof __dirname !== 'undefined') {\n return __dirname;\n }\n return dirname(fileURLToPath(import.meta.url));\n}\n\nconst _dirname = getDirname();\n\n/**\n * Simple path configuration for Kiro Memory\n */\n\n// Base directory - Kiro Memory data in home directory\n// Backward compat: if ~/.contextkit (old name) exists, use it; otherwise ~/.kiro-memory\nconst _legacyDir = join(homedir(), '.contextkit');\nconst _defaultDir = existsSync(_legacyDir) ? _legacyDir : join(homedir(), '.kiro-memory');\nexport const DATA_DIR = process.env.KIRO_MEMORY_DATA_DIR || process.env.CONTEXTKIT_DATA_DIR || _defaultDir;\n\n// Kiro config directory\nexport const KIRO_CONFIG_DIR = process.env.KIRO_CONFIG_DIR || join(homedir(), '.kiro');\n\n// Plugin installation directory\nexport const PLUGIN_ROOT = join(KIRO_CONFIG_DIR, 'plugins', 'kiro-memory');\n\n// Data subdirectories\nexport const ARCHIVES_DIR = join(DATA_DIR, 'archives');\nexport const LOGS_DIR = join(DATA_DIR, 'logs');\nexport const TRASH_DIR = join(DATA_DIR, 'trash');\nexport const BACKUPS_DIR = join(DATA_DIR, 'backups');\nexport const MODES_DIR = join(DATA_DIR, 'modes');\nexport const USER_SETTINGS_PATH = join(DATA_DIR, 'settings.json');\n// Backward compat: if the DB with the old name exists, use it\nconst _legacyDb = join(DATA_DIR, 'contextkit.db');\nexport const DB_PATH = existsSync(_legacyDb) ? _legacyDb : join(DATA_DIR, 'kiro-memory.db');\nexport const VECTOR_DB_DIR = join(DATA_DIR, 'vector-db');\n\n// Observer sessions directory\nexport const OBSERVER_SESSIONS_DIR = join(DATA_DIR, 'observer-sessions');\n\n// Kiro integration paths\nexport const KIRO_SETTINGS_PATH = join(KIRO_CONFIG_DIR, 'settings.json');\nexport const KIRO_CONTEXT_PATH = join(KIRO_CONFIG_DIR, 'context.md');\n\n/**\n * Get project-specific archive directory\n */\nexport function getProjectArchiveDir(projectName: string): string {\n return join(ARCHIVES_DIR, projectName);\n}\n\n/**\n * Get worker socket path for a session\n */\nexport function getWorkerSocketPath(sessionId: number): string {\n return join(DATA_DIR, `worker-${sessionId}.sock`);\n}\n\n/**\n * Ensure a directory exists\n */\nexport function ensureDir(dirPath: string): void {\n mkdirSync(dirPath, { recursive: true });\n}\n\n/**\n * Ensure all data directories exist\n */\nexport function ensureAllDataDirs(): void {\n ensureDir(DATA_DIR);\n ensureDir(ARCHIVES_DIR);\n ensureDir(LOGS_DIR);\n ensureDir(TRASH_DIR);\n ensureDir(BACKUPS_DIR);\n ensureDir(MODES_DIR);\n}\n\n/**\n * Ensure modes directory exists\n */\nexport function ensureModesDir(): void {\n ensureDir(MODES_DIR);\n}\n\n/**\n * Get current project name from git root or cwd\n */\nexport function getCurrentProjectName(): string {\n try {\n const gitRoot = execSync('git rev-parse --show-toplevel', {\n cwd: process.cwd(),\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'ignore'],\n windowsHide: true\n }).trim();\n return basename(gitRoot);\n } catch (error) {\n logger.debug('SYSTEM', 'Git root detection failed, using cwd basename', {\n cwd: process.cwd()\n }, error as Error);\n return basename(process.cwd());\n }\n}\n\n/**\n * Find package root directory\n */\nexport function getPackageRoot(): string {\n return join(_dirname, '..');\n}\n\n/**\n * Create a timestamped backup filename\n */\nexport function createBackupFilename(originalPath: string): string {\n const timestamp = new Date()\n .toISOString()\n .replace(/[:.]/g, '-')\n .replace('T', '_')\n .slice(0, 19);\n\n return `${originalPath}.backup.${timestamp}`;\n}\n", "/**\n * Structured Logger for Kiro Memory Worker Service\n * Provides readable, traceable logging with correlation IDs and data flow tracking\n */\n\nimport { appendFileSync, existsSync, mkdirSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\n\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n SILENT = 4\n}\n\nexport type Component = 'HOOK' | 'WORKER' | 'SDK' | 'PARSER' | 'DB' | 'SYSTEM' | 'HTTP' | 'SESSION' | 'CHROMA' | 'CHROMA_SYNC' | 'FOLDER_INDEX' | 'CONTEXT' | 'QUEUE' | 'EMBEDDING' | 'SEARCH' | 'VECTOR' | 'WEBHOOK' | 'BACKUP';\n\ninterface LogContext {\n sessionId?: number;\n memorySessionId?: string;\n correlationId?: string;\n [key: string]: any;\n}\n\n// Default data directory for Kiro Memory\nconst DEFAULT_DATA_DIR = join(homedir(), '.contextkit');\n\nclass Logger {\n private level: LogLevel | null = null;\n private useColor: boolean;\n private logFilePath: string | null = null;\n private logFileInitialized: boolean = false;\n\n constructor() {\n // Disable colors when output is not a TTY (e.g., PM2 logs)\n this.useColor = process.stdout.isTTY ?? false;\n }\n\n /**\n * Initialize log file path and ensure directory exists (lazy initialization)\n */\n private ensureLogFileInitialized(): void {\n if (this.logFileInitialized) return;\n this.logFileInitialized = true;\n\n try {\n const logsDir = join(DEFAULT_DATA_DIR, 'logs');\n\n // Ensure logs directory exists\n if (!existsSync(logsDir)) {\n mkdirSync(logsDir, { recursive: true });\n }\n\n // Create log file path with date\n const date = new Date().toISOString().split('T')[0];\n this.logFilePath = join(logsDir, `kiro-memory-${date}.log`);\n } catch (error) {\n console.error('[LOGGER] Failed to initialize log file:', error);\n this.logFilePath = null;\n }\n }\n\n /**\n * Lazy-load log level from settings file\n */\n private getLevel(): LogLevel {\n if (this.level === null) {\n try {\n const settingsPath = join(DEFAULT_DATA_DIR, 'settings.json');\n if (existsSync(settingsPath)) {\n const settingsData = readFileSync(settingsPath, 'utf-8');\n const settings = JSON.parse(settingsData);\n const envLevel = (settings.KIRO_MEMORY_LOG_LEVEL || settings.CONTEXTKIT_LOG_LEVEL || 'INFO').toUpperCase();\n this.level = LogLevel[envLevel as keyof typeof LogLevel] ?? LogLevel.INFO;\n } else {\n this.level = LogLevel.INFO;\n }\n } catch (error) {\n this.level = LogLevel.INFO;\n }\n }\n return this.level;\n }\n\n /**\n * Create correlation ID for tracking an observation through the pipeline\n */\n correlationId(sessionId: number, observationNum: number): string {\n return `obs-${sessionId}-${observationNum}`;\n }\n\n /**\n * Create session correlation ID\n */\n sessionId(sessionId: number): string {\n return `session-${sessionId}`;\n }\n\n /**\n * Format data for logging - create compact summaries instead of full dumps\n */\n private formatData(data: any): string {\n if (data === null || data === undefined) return '';\n if (typeof data === 'string') return data;\n if (typeof data === 'number') return data.toString();\n if (typeof data === 'boolean') return data.toString();\n\n if (typeof data === 'object') {\n if (data instanceof Error) {\n return this.getLevel() === LogLevel.DEBUG\n ? `${data.message}\\n${data.stack}`\n : data.message;\n }\n\n if (Array.isArray(data)) {\n return `[${data.length} items]`;\n }\n\n const keys = Object.keys(data);\n if (keys.length === 0) return '{}';\n if (keys.length <= 3) {\n return JSON.stringify(data);\n }\n return `{${keys.length} keys: ${keys.slice(0, 3).join(', ')}...}`;\n }\n\n return String(data);\n }\n\n /**\n * Format timestamp in local timezone (YYYY-MM-DD HH:MM:SS.mmm)\n */\n private formatTimestamp(date: Date): string {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const day = String(date.getDate()).padStart(2, '0');\n const hours = String(date.getHours()).padStart(2, '0');\n const minutes = String(date.getMinutes()).padStart(2, '0');\n const seconds = String(date.getSeconds()).padStart(2, '0');\n const ms = String(date.getMilliseconds()).padStart(3, '0');\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${ms}`;\n }\n\n /**\n * Core logging method\n */\n private log(\n level: LogLevel,\n component: Component,\n message: string,\n context?: LogContext,\n data?: any\n ): void {\n if (level < this.getLevel()) return;\n\n this.ensureLogFileInitialized();\n\n const timestamp = this.formatTimestamp(new Date());\n const levelStr = LogLevel[level].padEnd(5);\n const componentStr = component.padEnd(6);\n\n let correlationStr = '';\n if (context?.correlationId) {\n correlationStr = `[${context.correlationId}] `;\n } else if (context?.sessionId) {\n correlationStr = `[session-${context.sessionId}] `;\n }\n\n let dataStr = '';\n if (data !== undefined && data !== null) {\n if (data instanceof Error) {\n dataStr = this.getLevel() === LogLevel.DEBUG\n ? `\\n${data.message}\\n${data.stack}`\n : ` ${data.message}`;\n } else if (this.getLevel() === LogLevel.DEBUG && typeof data === 'object') {\n dataStr = '\\n' + JSON.stringify(data, null, 2);\n } else {\n dataStr = ' ' + this.formatData(data);\n }\n }\n\n let contextStr = '';\n if (context) {\n const { sessionId, memorySessionId, correlationId, ...rest } = context;\n if (Object.keys(rest).length > 0) {\n const pairs = Object.entries(rest).map(([k, v]) => `${k}=${v}`);\n contextStr = ` {${pairs.join(', ')}}`;\n }\n }\n\n const logLine = `[${timestamp}] [${levelStr}] [${componentStr}] ${correlationStr}${message}${contextStr}${dataStr}`;\n\n if (this.logFilePath) {\n try {\n appendFileSync(this.logFilePath, logLine + '\\n', 'utf8');\n } catch (error) {\n process.stderr.write(`[LOGGER] Failed to write to log file: ${error}\\n`);\n }\n } else {\n process.stderr.write(logLine + '\\n');\n }\n }\n\n // Public logging methods\n debug(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.DEBUG, component, message, context, data);\n }\n\n info(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.INFO, component, message, context, data);\n }\n\n warn(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.WARN, component, message, context, data);\n }\n\n error(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.ERROR, component, message, context, data);\n }\n\n /**\n * Log data flow: input \u2192 processing\n */\n dataIn(component: Component, message: string, context?: LogContext, data?: any): void {\n this.info(component, `\u2192 ${message}`, context, data);\n }\n\n /**\n * Log data flow: processing \u2192 output\n */\n dataOut(component: Component, message: string, context?: LogContext, data?: any): void {\n this.info(component, `\u2190 ${message}`, context, data);\n }\n\n /**\n * Log successful completion\n */\n success(component: Component, message: string, context?: LogContext, data?: any): void {\n this.info(component, `\u2713 ${message}`, context, data);\n }\n\n /**\n * Log failure\n */\n failure(component: Component, message: string, context?: LogContext, data?: any): void {\n this.error(component, `\u2717 ${message}`, context, data);\n }\n\n /**\n * Log timing information\n */\n timing(component: Component, message: string, durationMs: number, context?: LogContext): void {\n this.info(component, `\u23F1 ${message}`, context, { duration: `${durationMs}ms` });\n }\n\n /**\n * Happy Path Error - logs when the expected \"happy path\" fails but we have a fallback\n */\n happyPathError<T = string>(\n component: Component,\n message: string,\n context?: LogContext,\n data?: any,\n fallback: T = '' as T\n ): T {\n const stack = new Error().stack || '';\n const stackLines = stack.split('\\n');\n const callerLine = stackLines[2] || '';\n const callerMatch = callerLine.match(/at\\s+(?:.*\\s+)?\\(?([^:]+):(\\d+):(\\d+)\\)?/);\n const location = callerMatch\n ? `${callerMatch[1].split('/').pop()}:${callerMatch[2]}`\n : 'unknown';\n\n const enhancedContext = {\n ...context,\n location\n };\n\n this.warn(component, `[HAPPY-PATH] ${message}`, enhancedContext, data);\n\n return fallback;\n }\n}\n\n// Export singleton instance\nexport const logger = new Logger();\n", "import { Database } from 'bun:sqlite';\nimport { DATA_DIR, DB_PATH, ensureDir } from '../../shared/paths.js';\nimport { logger } from '../../utils/logger.js';\n\n// SQLite configuration constants\nconst SQLITE_MMAP_SIZE_BYTES = 256 * 1024 * 1024; // 256MB\nconst SQLITE_CACHE_SIZE_PAGES = 10_000;\n\nexport interface Migration {\n version: number;\n up: (db: Database) => void;\n down?: (db: Database) => void;\n}\n\n\n\n/**\n * KiroMemoryDatabase - Main entry point for the sqlite module\n *\n * Sets up bun:sqlite with optimized settings and runs all migrations.\n *\n * Usage:\n * const db = new KiroMemoryDatabase(); // uses default DB_PATH\n * const db = new KiroMemoryDatabase('/path/to/db.sqlite');\n * const db = new KiroMemoryDatabase(':memory:'); // for tests\n */\nexport class KiroMemoryDatabase {\n private _db: Database;\n\n /**\n * Readonly accessor for the underlying Database instance.\n * Prefer using query() and run() proxy methods directly.\n */\n get db(): Database {\n return this._db;\n }\n\n /**\n * @param dbPath - Path to the SQLite file (default: DB_PATH)\n * @param skipMigrations - If true, skip the migration runner (for high-frequency hooks)\n */\n constructor(dbPath: string = DB_PATH, skipMigrations: boolean = false) {\n // Ensure data directory exists (skip for in-memory databases)\n if (dbPath !== ':memory:') {\n ensureDir(DATA_DIR);\n }\n\n // Create database connection\n this._db = new Database(dbPath, { create: true, readwrite: true });\n\n // Apply optimized SQLite settings\n this._db.run('PRAGMA journal_mode = WAL');\n this._db.run('PRAGMA busy_timeout = 5000'); // Wait up to 5s on concurrent lock (hook + worker)\n this._db.run('PRAGMA synchronous = NORMAL');\n this._db.run('PRAGMA foreign_keys = ON');\n this._db.run('PRAGMA temp_store = memory');\n this._db.run(`PRAGMA mmap_size = ${SQLITE_MMAP_SIZE_BYTES}`);\n this._db.run(`PRAGMA cache_size = ${SQLITE_CACHE_SIZE_PAGES}`);\n\n // Run migrations only if needed (hooks skip them for performance)\n if (!skipMigrations) {\n const migrationRunner = new MigrationRunner(this._db);\n migrationRunner.runAllMigrations();\n }\n }\n\n /**\n * Prepare a query (delegates to underlying Database).\n * Proxy method to avoid ctx.db.db.query() double access.\n */\n query(sql: string) {\n return this._db.query(sql);\n }\n\n /**\n * Execute a SQL statement without results (delegates to underlying Database).\n * Proxy method to avoid ctx.db.db.run() double access.\n */\n run(sql: string, params?: any[]) {\n return this._db.run(sql, params);\n }\n\n /**\n * Executes a function within an atomic transaction.\n * If fn() throws an error, the transaction is automatically rolled back.\n */\n withTransaction<T>(fn: (db: Database) => T): T {\n const transaction = this._db.transaction(fn);\n return transaction(this._db);\n }\n\n /**\n * Close the database connection\n */\n close(): void {\n this._db.close();\n }\n}\n\n/**\n * Migration runner for Kiro Memory\n */\nclass MigrationRunner {\n private db: Database;\n\n constructor(db: Database) {\n this.db = db;\n }\n\n runAllMigrations(): void {\n // Create schema_versions table if not exists\n this.db.run(`\n CREATE TABLE IF NOT EXISTS schema_versions (\n id INTEGER PRIMARY KEY,\n version INTEGER UNIQUE NOT NULL,\n applied_at TEXT NOT NULL\n )\n `);\n\n // Get current version\n const versionQuery = this.db.query('SELECT MAX(version) as version FROM schema_versions');\n const result = versionQuery.get() as { version: number } | null;\n const currentVersion = result?.version || 0;\n\n // Run migrations\n const migrations = this.getMigrations();\n for (const migration of migrations) {\n if (migration.version > currentVersion) {\n logger.info('DB', `Applying migration ${migration.version}`);\n \n const transaction = this.db.transaction(() => {\n migration.up(this.db);\n const insert = this.db.query('INSERT INTO schema_versions (version, applied_at) VALUES (?, ?)');\n insert.run(migration.version, new Date().toISOString());\n });\n \n transaction();\n logger.info('DB', `Migration ${migration.version} applied successfully`);\n }\n }\n }\n\n private getMigrations(): Migration[] {\n return [\n {\n version: 1,\n up: (db) => {\n // Sessions table\n db.run(`\n CREATE TABLE IF NOT EXISTS sessions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n content_session_id TEXT NOT NULL UNIQUE,\n project TEXT NOT NULL,\n user_prompt TEXT NOT NULL,\n memory_session_id TEXT,\n status TEXT DEFAULT 'active',\n started_at TEXT NOT NULL,\n started_at_epoch INTEGER NOT NULL,\n completed_at TEXT,\n completed_at_epoch INTEGER\n )\n `);\n\n // Observations table\n db.run(`\n CREATE TABLE IF NOT EXISTS observations (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n memory_session_id TEXT NOT NULL,\n project TEXT NOT NULL,\n type TEXT NOT NULL,\n title TEXT NOT NULL,\n subtitle TEXT,\n text TEXT,\n narrative TEXT,\n facts TEXT,\n concepts TEXT,\n files_read TEXT,\n files_modified TEXT,\n prompt_number INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n created_at_epoch INTEGER NOT NULL\n )\n `);\n\n // Summaries table\n db.run(`\n CREATE TABLE IF NOT EXISTS summaries (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL,\n project TEXT NOT NULL,\n request TEXT,\n investigated TEXT,\n learned TEXT,\n completed TEXT,\n next_steps TEXT,\n notes TEXT,\n created_at TEXT NOT NULL,\n created_at_epoch INTEGER NOT NULL\n )\n `);\n\n // Prompts table\n db.run(`\n CREATE TABLE IF NOT EXISTS prompts (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n content_session_id TEXT NOT NULL,\n project TEXT NOT NULL,\n prompt_number INTEGER NOT NULL,\n prompt_text TEXT NOT NULL,\n created_at TEXT NOT NULL,\n created_at_epoch INTEGER NOT NULL\n )\n `);\n\n // Pending messages table\n db.run(`\n CREATE TABLE IF NOT EXISTS pending_messages (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n content_session_id TEXT NOT NULL,\n type TEXT NOT NULL,\n data TEXT NOT NULL,\n created_at TEXT NOT NULL,\n created_at_epoch INTEGER NOT NULL\n )\n `);\n\n // Indexes\n db.run('CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project)');\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_project ON observations(project)');\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_session ON observations(memory_session_id)');\n db.run('CREATE INDEX IF NOT EXISTS idx_summaries_session ON summaries(session_id)');\n db.run('CREATE INDEX IF NOT EXISTS idx_prompts_session ON prompts(content_session_id)');\n }\n },\n {\n version: 2,\n up: (db) => {\n // FTS5 table for full-text search on observations\n db.run(`\n CREATE VIRTUAL TABLE IF NOT EXISTS observations_fts USING fts5(\n title, text, narrative, concepts,\n content='observations',\n content_rowid='id'\n )\n `);\n\n // Triggers to keep FTS5 synchronized\n db.run(`\n CREATE TRIGGER IF NOT EXISTS observations_ai AFTER INSERT ON observations BEGIN\n INSERT INTO observations_fts(rowid, title, text, narrative, concepts)\n VALUES (new.id, new.title, new.text, new.narrative, new.concepts);\n END\n `);\n\n db.run(`\n CREATE TRIGGER IF NOT EXISTS observations_ad AFTER DELETE ON observations BEGIN\n INSERT INTO observations_fts(observations_fts, rowid, title, text, narrative, concepts)\n VALUES ('delete', old.id, old.title, old.text, old.narrative, old.concepts);\n END\n `);\n\n db.run(`\n CREATE TRIGGER IF NOT EXISTS observations_au AFTER UPDATE ON observations BEGIN\n INSERT INTO observations_fts(observations_fts, rowid, title, text, narrative, concepts)\n VALUES ('delete', old.id, old.title, old.text, old.narrative, old.concepts);\n INSERT INTO observations_fts(rowid, title, text, narrative, concepts)\n VALUES (new.id, new.title, new.text, new.narrative, new.concepts);\n END\n `);\n\n // Backfill existing observations into the FTS5 table\n db.run(`\n INSERT INTO observations_fts(rowid, title, text, narrative, concepts)\n SELECT id, title, text, narrative, concepts FROM observations\n `);\n\n // Additional indexes for search performance\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_type ON observations(type)');\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_epoch ON observations(created_at_epoch)');\n db.run('CREATE INDEX IF NOT EXISTS idx_summaries_project ON summaries(project)');\n db.run('CREATE INDEX IF NOT EXISTS idx_summaries_epoch ON summaries(created_at_epoch)');\n db.run('CREATE INDEX IF NOT EXISTS idx_prompts_project ON prompts(project)');\n }\n },\n {\n version: 3,\n up: (db) => {\n // Alias table for renaming projects in the UI\n db.run(`\n CREATE TABLE IF NOT EXISTS project_aliases (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_name TEXT NOT NULL UNIQUE,\n display_name TEXT NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n )\n `);\n\n db.run('CREATE UNIQUE INDEX IF NOT EXISTS idx_project_aliases_name ON project_aliases(project_name)');\n }\n },\n {\n version: 4,\n up: (db) => {\n // Embeddings table for local semantic search\n db.run(`\n CREATE TABLE IF NOT EXISTS observation_embeddings (\n observation_id INTEGER PRIMARY KEY,\n embedding BLOB NOT NULL,\n model TEXT NOT NULL,\n dimensions INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n FOREIGN KEY (observation_id) REFERENCES observations(id) ON DELETE CASCADE\n )\n `);\n\n db.run('CREATE INDEX IF NOT EXISTS idx_embeddings_model ON observation_embeddings(model)');\n }\n },\n {\n version: 5,\n up: (db) => {\n // Track last access (search that found the observation)\n db.run('ALTER TABLE observations ADD COLUMN last_accessed_epoch INTEGER');\n // Stale flag: 0 = fresh, 1 = file modified after the observation\n db.run('ALTER TABLE observations ADD COLUMN is_stale INTEGER DEFAULT 0');\n // Index for decay queries\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_last_accessed ON observations(last_accessed_epoch)');\n // Index for stale queries\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_stale ON observations(is_stale)');\n }\n },\n {\n version: 6,\n up: (db) => {\n // Checkpoint table for session resume\n db.run(`\n CREATE TABLE IF NOT EXISTS checkpoints (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id INTEGER NOT NULL,\n project TEXT NOT NULL,\n task TEXT NOT NULL,\n progress TEXT,\n next_steps TEXT,\n open_questions TEXT,\n relevant_files TEXT,\n context_snapshot TEXT,\n created_at TEXT NOT NULL,\n created_at_epoch INTEGER NOT NULL,\n FOREIGN KEY (session_id) REFERENCES sessions(id)\n )\n `);\n db.run('CREATE INDEX IF NOT EXISTS idx_checkpoints_session ON checkpoints(session_id)');\n db.run('CREATE INDEX IF NOT EXISTS idx_checkpoints_project ON checkpoints(project)');\n db.run('CREATE INDEX IF NOT EXISTS idx_checkpoints_epoch ON checkpoints(created_at_epoch)');\n }\n },\n {\n version: 7,\n up: (db) => {\n // Content hash for content-based deduplication (SHA256)\n db.run('ALTER TABLE observations ADD COLUMN content_hash TEXT');\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_hash ON observations(content_hash)');\n }\n },\n {\n version: 8,\n up: (db) => {\n // Token economics: tokens spent to generate the observation (discovery cost)\n db.run('ALTER TABLE observations ADD COLUMN discovery_tokens INTEGER DEFAULT 0');\n // Token economics on summaries\n db.run('ALTER TABLE summaries ADD COLUMN discovery_tokens INTEGER DEFAULT 0');\n }\n },\n {\n version: 9,\n up: (db) => {\n // Composite indexes for pagination and project filters\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_project_epoch ON observations(project, created_at_epoch DESC)');\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_project_type ON observations(project, type)');\n db.run('CREATE INDEX IF NOT EXISTS idx_summaries_project_epoch ON summaries(project, created_at_epoch DESC)');\n db.run('CREATE INDEX IF NOT EXISTS idx_prompts_project_epoch ON prompts(project, created_at_epoch DESC)');\n }\n },\n {\n version: 10,\n up: (db) => {\n // Async job queue for background processing (embeddings, consolidation, backups)\n db.run(`\n CREATE TABLE IF NOT EXISTS job_queue (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n type TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending',\n payload TEXT,\n result TEXT,\n error TEXT,\n retry_count INTEGER DEFAULT 0,\n max_retries INTEGER DEFAULT 3,\n priority INTEGER DEFAULT 0,\n created_at TEXT NOT NULL,\n created_at_epoch INTEGER NOT NULL,\n started_at_epoch INTEGER,\n completed_at_epoch INTEGER\n )\n `);\n db.run('CREATE INDEX IF NOT EXISTS idx_jobs_status ON job_queue(status)');\n db.run('CREATE INDEX IF NOT EXISTS idx_jobs_type ON job_queue(type)');\n // Composite index for priority-based polling: pending jobs ordered by priority DESC, then FIFO\n db.run('CREATE INDEX IF NOT EXISTS idx_jobs_priority ON job_queue(status, priority DESC, created_at_epoch ASC)');\n }\n },\n {\n version: 11,\n up: (db) => {\n // Colonna auto-category per classificazione basata su keyword\n db.run('ALTER TABLE observations ADD COLUMN auto_category TEXT');\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_category ON observations(auto_category)');\n }\n },\n {\n version: 12,\n up: (db) => {\n // Tabella per i link GitHub (webhook events: issues, PR, push)\n db.run(`\n CREATE TABLE IF NOT EXISTS github_links (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n observation_id INTEGER,\n session_id TEXT,\n repo TEXT NOT NULL,\n issue_number INTEGER,\n pr_number INTEGER,\n event_type TEXT NOT NULL,\n action TEXT,\n title TEXT,\n url TEXT,\n author TEXT,\n created_at TEXT NOT NULL,\n created_at_epoch INTEGER NOT NULL,\n FOREIGN KEY (observation_id) REFERENCES observations(id)\n )\n `);\n // Indice per query per repository\n db.run('CREATE INDEX IF NOT EXISTS idx_github_links_repo ON github_links(repo)');\n // Indice per join con observations\n db.run('CREATE INDEX IF NOT EXISTS idx_github_links_obs ON github_links(observation_id)');\n // Indice per ricerche per tipo di evento\n db.run('CREATE INDEX IF NOT EXISTS idx_github_links_event ON github_links(event_type)');\n // Indice composto per query per issue/PR all\\'interno di un repo\n db.run('CREATE INDEX IF NOT EXISTS idx_github_links_repo_issue ON github_links(repo, issue_number)');\n db.run('CREATE INDEX IF NOT EXISTS idx_github_links_repo_pr ON github_links(repo, pr_number)');\n }\n },\n {\n version: 13,\n up: (db) => {\n // Indici compositi (created_at_epoch DESC, id DESC) per keyset pagination efficiente.\n // Permettono WHERE (created_at_epoch, id) < (?, ?) senza full-scan.\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_keyset ON observations(created_at_epoch DESC, id DESC)');\n db.run('CREATE INDEX IF NOT EXISTS idx_observations_project_keyset ON observations(project, created_at_epoch DESC, id DESC)');\n db.run('CREATE INDEX IF NOT EXISTS idx_summaries_keyset ON summaries(created_at_epoch DESC, id DESC)');\n db.run('CREATE INDEX IF NOT EXISTS idx_summaries_project_keyset ON summaries(project, created_at_epoch DESC, id DESC)');\n db.run('CREATE INDEX IF NOT EXISTS idx_prompts_keyset ON prompts(created_at_epoch DESC, id DESC)');\n db.run('CREATE INDEX IF NOT EXISTS idx_prompts_project_keyset ON prompts(project, created_at_epoch DESC, id DESC)');\n }\n }\n ];\n }\n}\n\n// Re-export bun:sqlite Database type\nexport { Database };\n", "/**\n * Local embedding service for Kiro Memory\n *\n * Provider: fastembed (primary, only for compatible models) \u2192 @huggingface/transformers (fallback) \u2192 null (FTS5 only)\n * Generates vector embeddings for semantic search.\n * Lazy loading: the model is loaded only on first use.\n *\n * Configurable via environment variables:\n * KIRO_MEMORY_EMBEDDING_MODEL \u2014 model name or full HuggingFace ID (default: 'all-MiniLM-L6-v2')\n * KIRO_MEMORY_EMBEDDING_DIMENSIONS \u2014 fallback dimensions for unknown custom models (default: 384)\n */\n\nimport { logger } from '../../utils/logger.js';\n\ntype EmbeddingProvider = 'fastembed' | 'transformers' | null;\n\n/** Configuration for an embedding model. */\nexport interface EmbeddingConfig {\n /** Model ID for @huggingface/transformers (e.g., 'Xenova/all-MiniLM-L6-v2', 'jinaai/jina-embeddings-v2-base-code') */\n modelId: string;\n /** Expected dimensions of the embedding vector */\n dimensions: number;\n}\n\n/** Built-in model configurations keyed by short name. */\nconst MODEL_CONFIGS: Record<string, EmbeddingConfig> = {\n 'all-MiniLM-L6-v2': {\n modelId: 'Xenova/all-MiniLM-L6-v2',\n dimensions: 384,\n },\n 'jina-code-v2': {\n modelId: 'jinaai/jina-embeddings-v2-base-code',\n dimensions: 768,\n },\n 'bge-small-en': {\n modelId: 'BAAI/bge-small-en-v1.5',\n dimensions: 384,\n },\n};\n\n/**\n * Models that fastembed supports natively.\n * fastembed only supports BGE-small and all-MiniLM internally; arbitrary HF models are not supported.\n */\nconst FASTEMBED_COMPATIBLE_MODELS = new Set(['all-MiniLM-L6-v2', 'bge-small-en']);\n\nexport class EmbeddingService {\n private provider: EmbeddingProvider = null;\n private model: any = null;\n private initialized = false;\n private initializing: Promise<boolean> | null = null;\n private config: EmbeddingConfig;\n private configName: string;\n\n constructor() {\n const envModel = process.env.KIRO_MEMORY_EMBEDDING_MODEL || 'all-MiniLM-L6-v2';\n this.configName = envModel;\n\n if (MODEL_CONFIGS[envModel]) {\n // Known short name \u2014 use predefined config\n this.config = MODEL_CONFIGS[envModel];\n } else if (envModel.includes('/')) {\n // Full HuggingFace model ID (e.g., 'custom/my-model') \u2014 use directly\n const dimensions = parseInt(process.env.KIRO_MEMORY_EMBEDDING_DIMENSIONS || '384', 10);\n this.config = {\n modelId: envModel,\n dimensions: isNaN(dimensions) ? 384 : dimensions,\n };\n } else {\n // Unknown short name \u2014 fall back to default\n logger.warn('EMBEDDING', `Unknown model name '${envModel}', falling back to 'all-MiniLM-L6-v2'`);\n this.configName = 'all-MiniLM-L6-v2';\n this.config = MODEL_CONFIGS['all-MiniLM-L6-v2'];\n }\n }\n\n /**\n * Initialize the embedding service.\n * Tries fastembed (when compatible), then @huggingface/transformers, then falls back to null.\n */\n async initialize(): Promise<boolean> {\n if (this.initialized) return this.provider !== null;\n\n // Avoid concurrent initializations\n if (this.initializing) return this.initializing;\n\n this.initializing = this._doInitialize();\n const result = await this.initializing;\n this.initializing = null;\n return result;\n }\n\n private async _doInitialize(): Promise<boolean> {\n // Attempt 1: fastembed \u2014 only for compatible models (fastembed does not support arbitrary HF models)\n const fastembedCompatible = FASTEMBED_COMPATIBLE_MODELS.has(this.configName);\n if (fastembedCompatible) {\n try {\n const fastembed = await import('fastembed');\n const EmbeddingModel = fastembed.EmbeddingModel || fastembed.default?.EmbeddingModel;\n const FlagEmbedding = fastembed.FlagEmbedding || fastembed.default?.FlagEmbedding;\n\n if (FlagEmbedding && EmbeddingModel) {\n this.model = await FlagEmbedding.init({\n model: EmbeddingModel.BGESmallENV15\n });\n this.provider = 'fastembed';\n this.initialized = true;\n logger.info('EMBEDDING', `Initialized with fastembed (BGE-small-en-v1.5) for model '${this.configName}'`);\n return true;\n }\n } catch (error) {\n logger.debug('EMBEDDING', `fastembed not available: ${error}`);\n }\n }\n\n // Attempt 2: @huggingface/transformers with the configured model ID\n try {\n const transformers = await import('@huggingface/transformers');\n const pipeline = (transformers as any).pipeline || (transformers as any).default?.pipeline;\n\n if (pipeline) {\n this.model = await pipeline('feature-extraction', this.config.modelId, {\n quantized: true\n } as any);\n this.provider = 'transformers';\n this.initialized = true;\n logger.info('EMBEDDING', `Initialized with @huggingface/transformers (${this.config.modelId})`);\n return true;\n }\n } catch (error) {\n logger.debug('EMBEDDING', `@huggingface/transformers not available: ${error}`);\n }\n\n // No provider available\n this.provider = null;\n this.initialized = true;\n logger.warn('EMBEDDING', 'No embedding provider available, semantic search disabled');\n return false;\n }\n\n /**\n * Generate embedding for a single text.\n * Returns Float32Array with configured dimensions, or null if not available.\n */\n async embed(text: string): Promise<Float32Array | null> {\n if (!this.initialized) await this.initialize();\n if (!this.provider || !this.model) return null;\n\n try {\n // Truncate text that is too long (max ~512 tokens \u2248 2000 chars)\n const truncated = text.substring(0, 2000);\n\n if (this.provider === 'fastembed') {\n return await this._embedFastembed(truncated);\n } else if (this.provider === 'transformers') {\n return await this._embedTransformers(truncated);\n }\n } catch (error) {\n logger.error('EMBEDDING', `Error generating embedding: ${error}`);\n }\n\n return null;\n }\n\n /**\n * Generate embeddings in batch.\n * Uses native batch support when available (fastembed, transformers),\n * falls back to serial processing on batch failure.\n */\n async embedBatch(texts: string[]): Promise<(Float32Array | null)[]> {\n if (!this.initialized) await this.initialize();\n if (!this.provider || !this.model) return texts.map(() => null);\n if (texts.length === 0) return [];\n\n // Truncate all texts upfront (max ~512 tokens \u2248 2000 chars)\n const truncated = texts.map(t => t.substring(0, 2000));\n\n // Try native batch embedding first\n try {\n if (this.provider === 'fastembed') {\n return await this._embedBatchFastembed(truncated);\n } else if (this.provider === 'transformers') {\n return await this._embedBatchTransformers(truncated);\n }\n } catch (error) {\n logger.warn('EMBEDDING', `Batch embedding failed, falling back to serial: ${error}`);\n }\n\n // Serial fallback: process one at a time\n return this._embedBatchSerial(truncated);\n }\n\n /**\n * Check if the service is available.\n */\n isAvailable(): boolean {\n return this.initialized && this.provider !== null;\n }\n\n /**\n * Name of the active provider.\n */\n getProvider(): string | null {\n return this.provider;\n }\n\n /**\n * Embedding vector dimensions for the active model configuration.\n */\n getDimensions(): number {\n return this.config.dimensions;\n }\n\n /**\n * Human-readable model name used as identifier in the observation_embeddings table.\n * Returns the short name (e.g., 'all-MiniLM-L6-v2') or the full HF model ID for custom models.\n */\n getModelName(): string {\n return this.configName;\n }\n\n // --- Batch implementations ---\n\n /**\n * Native batch embedding with fastembed.\n * FlagEmbedding.embed() accepts string[] and returns an async iterable of batches.\n */\n private async _embedBatchFastembed(texts: string[]): Promise<(Float32Array | null)[]> {\n const results: (Float32Array | null)[] = [];\n // Pass entire array at once; second param is internal batch size for chunking\n const embeddings = this.model.embed(texts, texts.length);\n for await (const batch of embeddings) {\n if (batch) {\n for (const vec of batch) {\n results.push(vec instanceof Float32Array ? vec : new Float32Array(vec));\n }\n }\n }\n // Pad with nulls if fastembed returned fewer results than expected\n while (results.length < texts.length) {\n results.push(null);\n }\n return results;\n }\n\n /**\n * Batch embedding with @huggingface/transformers pipeline.\n * The pipeline accepts string[] and returns a Tensor with shape [N, dims].\n */\n private async _embedBatchTransformers(texts: string[]): Promise<(Float32Array | null)[]> {\n const output = await this.model(texts, {\n pooling: 'mean',\n normalize: true\n });\n\n if (!output?.data) {\n return texts.map(() => null);\n }\n\n const dims = this.getDimensions();\n const data = output.data instanceof Float32Array\n ? output.data\n : new Float32Array(output.data);\n\n // The output tensor is flattened [N * dims], slice into individual vectors\n const results: (Float32Array | null)[] = [];\n for (let i = 0; i < texts.length; i++) {\n const offset = i * dims;\n if (offset + dims <= data.length) {\n results.push(data.slice(offset, offset + dims));\n } else {\n results.push(null);\n }\n }\n return results;\n }\n\n /**\n * Serial fallback: embed texts one at a time.\n * Used when native batch fails.\n */\n private async _embedBatchSerial(texts: string[]): Promise<(Float32Array | null)[]> {\n const results: (Float32Array | null)[] = [];\n for (const text of texts) {\n try {\n const embedding = await this.embed(text);\n results.push(embedding);\n } catch {\n results.push(null);\n }\n }\n return results;\n }\n\n // --- Single-text provider implementations ---\n\n private async _embedFastembed(text: string): Promise<Float32Array | null> {\n const embeddings = this.model.embed([text], 1);\n for await (const batch of embeddings) {\n if (batch && batch.length > 0) {\n // fastembed returns array of arrays\n const vec = batch[0];\n return vec instanceof Float32Array ? vec : new Float32Array(vec);\n }\n }\n return null;\n }\n\n private async _embedTransformers(text: string): Promise<Float32Array | null> {\n const output = await this.model(text, {\n pooling: 'mean',\n normalize: true\n });\n\n // transformers.js returns a Tensor, extract the data\n if (output?.data) {\n return output.data instanceof Float32Array\n ? output.data\n : new Float32Array(output.data);\n }\n\n return null;\n }\n}\n\n// Singleton\nlet embeddingService: EmbeddingService | null = null;\n\nexport function getEmbeddingService(): EmbeddingService {\n if (!embeddingService) {\n embeddingService = new EmbeddingService();\n }\n return embeddingService;\n}\n", "/**\n * Local vector search on SQLite BLOB\n *\n * Stores embeddings as BLOB in observation_embeddings,\n * computes cosine similarity in JavaScript for semantic search.\n *\n * Optimizations vs brute-force O(n):\n * - SQL pre-filtering by project and recency (reduces candidates)\n * - Maximum limit of candidates loaded in memory (maxCandidates)\n * - Buffer pooling to reduce GC allocations\n */\n\nimport type { Database } from 'bun:sqlite';\nimport { getEmbeddingService } from './EmbeddingService.js';\nimport { logger } from '../../utils/logger.js';\n\n// Maximum number of embeddings loaded in memory per query\nconst DEFAULT_MAX_CANDIDATES = 2000;\n\nexport interface VectorSearchResult {\n id: number;\n observationId: number;\n similarity: number;\n title: string;\n text: string | null;\n type: string;\n project: string;\n created_at: string;\n created_at_epoch: number;\n}\n\n/**\n * Compute cosine similarity between two Float32Array vectors.\n * Optimized version: unified loop with a single final sqrt.\n */\nexport function cosineSimilarity(a: Float32Array, b: Float32Array): number {\n const len = a.length;\n if (len !== b.length) return 0;\n\n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n\n // Unified loop \u2014 avoids 3 separate passes\n for (let i = 0; i < len; i++) {\n const ai = a[i];\n const bi = b[i];\n dotProduct += ai * bi;\n normA += ai * ai;\n normB += bi * bi;\n }\n\n const denominator = Math.sqrt(normA * normB);\n if (denominator === 0) return 0;\n\n return dotProduct / denominator;\n}\n\n/**\n * Convert Float32Array to Buffer for SQLite BLOB storage.\n */\nfunction float32ToBuffer(arr: Float32Array): Buffer {\n return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/**\n * Convert SQLite BLOB Buffer to Float32Array.\n */\nfunction bufferToFloat32(buf: Buffer | Uint8Array): Float32Array {\n const arrayBuffer = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);\n return new Float32Array(arrayBuffer);\n}\n\nexport class VectorSearch {\n\n /**\n * Semantic search with SQL pre-filtering for scalability.\n *\n * 2-phase strategy:\n * 1. SQL pre-filters by project + sorts by recency (loads max N candidates)\n * 2. JS computes cosine similarity only on filtered candidates\n *\n * With 50k observations and maxCandidates=2000, loads only ~4% of data.\n */\n async search(\n db: Database,\n queryEmbedding: Float32Array,\n options: {\n project?: string;\n limit?: number;\n threshold?: number;\n maxCandidates?: number;\n } = {}\n ): Promise<VectorSearchResult[]> {\n const limit = options.limit || 10;\n const threshold = options.threshold || 0.3;\n const maxCandidates = options.maxCandidates || DEFAULT_MAX_CANDIDATES;\n\n try {\n // Phase 1: pre-filter in SQL by project, sort by recency, limit candidates\n const conditions: string[] = [];\n const params: any[] = [];\n\n if (options.project) {\n conditions.push('o.project = ?');\n params.push(options.project);\n }\n\n const whereClause = conditions.length > 0\n ? `WHERE ${conditions.join(' AND ')}`\n : '';\n\n // Sort by recency and limit candidates \u2014 avoids loading all embeddings\n const sql = `\n SELECT e.observation_id, e.embedding,\n o.title, o.text, o.type, o.project, o.created_at, o.created_at_epoch\n FROM observation_embeddings e\n JOIN observations o ON o.id = e.observation_id\n ${whereClause}\n ORDER BY o.created_at_epoch DESC\n LIMIT ?\n `;\n params.push(maxCandidates);\n\n const rows = db.query(sql).all(...params) as Array<{\n observation_id: number;\n embedding: Buffer;\n title: string;\n text: string | null;\n type: string;\n project: string;\n created_at: string;\n created_at_epoch: number;\n }>;\n\n // Phase 2: compute similarity only on pre-filtered candidates\n const scored: VectorSearchResult[] = [];\n\n for (const row of rows) {\n const embedding = bufferToFloat32(row.embedding);\n const similarity = cosineSimilarity(queryEmbedding, embedding);\n\n if (similarity >= threshold) {\n scored.push({\n id: row.observation_id,\n observationId: row.observation_id,\n similarity,\n title: row.title,\n text: row.text,\n type: row.type,\n project: row.project,\n created_at: row.created_at,\n created_at_epoch: row.created_at_epoch\n });\n }\n }\n\n // Sort by similarity descending\n scored.sort((a, b) => b.similarity - a.similarity);\n\n logger.debug('VECTOR', `Search: ${rows.length} candidates \u2192 ${scored.length} above threshold \u2192 ${Math.min(scored.length, limit)} results`);\n\n return scored.slice(0, limit);\n } catch (error) {\n logger.error('VECTOR', `Vector search error: ${error}`);\n return [];\n }\n }\n\n /**\n * Store embedding for an observation.\n */\n async storeEmbedding(\n db: Database,\n observationId: number,\n embedding: Float32Array,\n model: string\n ): Promise<void> {\n try {\n const blob = float32ToBuffer(embedding);\n\n db.query(`\n INSERT OR REPLACE INTO observation_embeddings\n (observation_id, embedding, model, dimensions, created_at)\n VALUES (?, ?, ?, ?, ?)\n `).run(\n observationId,\n blob,\n model,\n embedding.length,\n new Date().toISOString()\n );\n\n logger.debug('VECTOR', `Embedding saved for observation ${observationId}`);\n } catch (error) {\n logger.error('VECTOR', `Error saving embedding: ${error}`);\n }\n }\n\n /**\n * Generate embeddings for observations that don't have them yet.\n */\n async backfillEmbeddings(\n db: Database,\n batchSize: number = 50\n ): Promise<number> {\n const embeddingService = getEmbeddingService();\n if (!await embeddingService.initialize()) {\n logger.warn('VECTOR', 'Embedding service not available, backfill skipped');\n return 0;\n }\n\n // Find observations without embeddings\n const rows = db.query(`\n SELECT o.id, o.title, o.text, o.narrative, o.concepts\n FROM observations o\n LEFT JOIN observation_embeddings e ON e.observation_id = o.id\n WHERE e.observation_id IS NULL\n ORDER BY o.created_at_epoch DESC\n LIMIT ?\n `).all(batchSize) as Array<{\n id: number;\n title: string;\n text: string | null;\n narrative: string | null;\n concepts: string | null;\n }>;\n\n if (rows.length === 0) return 0;\n\n let count = 0;\n const model = embeddingService.getModelName();\n\n for (const row of rows) {\n // Compose text for embedding: title + text + concepts\n const parts = [row.title];\n if (row.text) parts.push(row.text);\n if (row.narrative) parts.push(row.narrative);\n if (row.concepts) parts.push(row.concepts);\n const fullText = parts.join(' ').substring(0, 2000);\n\n const embedding = await embeddingService.embed(fullText);\n if (embedding) {\n await this.storeEmbedding(db, row.id, embedding, model);\n count++;\n }\n }\n\n logger.info('VECTOR', `Backfill completed: ${count}/${rows.length} embeddings generated`);\n return count;\n }\n\n /**\n * Embedding statistics.\n */\n getStats(db: Database): { total: number; embedded: number; percentage: number } {\n try {\n const totalRow = db.query('SELECT COUNT(*) as count FROM observations').get() as { count: number };\n const embeddedRow = db.query('SELECT COUNT(*) as count FROM observation_embeddings').get() as { count: number };\n\n const total = totalRow?.count || 0;\n const embedded = embeddedRow?.count || 0;\n const percentage = total > 0 ? Math.round((embedded / total) * 100) : 0;\n\n return { total, embedded, percentage };\n } catch {\n return { total: 0, embedded: 0, percentage: 0 };\n }\n }\n}\n\n// Singleton\nlet vectorSearch: VectorSearch | null = null;\n\nexport function getVectorSearch(): VectorSearch {\n if (!vectorSearch) {\n vectorSearch = new VectorSearch();\n }\n return vectorSearch;\n}\n", "/**\n * Scoring engine for intelligent ranking\n *\n * Pure functions with no DB dependencies. Combines 4 signals:\n * - semantic: cosine similarity from embedding\n * - fts5: normalized FTS5 rank\n * - recency: exponential decay based on age\n * - projectMatch: 1 if project matches, 0 otherwise\n */\n\nimport type { ScoringWeights } from '../../types/worker-types.js';\n\n/** Weights for search mode (with text query) */\nexport const SEARCH_WEIGHTS: ScoringWeights = {\n semantic: 0.4,\n fts5: 0.3,\n recency: 0.2,\n projectMatch: 0.1\n};\n\n/** Weights for context mode (no query, e.g. agentSpawn) */\nexport const CONTEXT_WEIGHTS: ScoringWeights = {\n semantic: 0.0,\n fts5: 0.0,\n recency: 0.7,\n projectMatch: 0.3\n};\n\n/**\n * Calculate recency score with exponential decay.\n * More recent = higher (close to 1). After halfLifeHours the score is ~0.5.\n *\n * @param createdAtEpoch - Creation timestamp in milliseconds\n * @param halfLifeHours - Half-life in hours (default: 168 = 7 days)\n * @returns Score 0-1\n */\nexport function recencyScore(createdAtEpoch: number, halfLifeHours: number = 168): number {\n if (!createdAtEpoch || createdAtEpoch <= 0) return 0;\n\n const nowMs = Date.now();\n const ageMs = nowMs - createdAtEpoch;\n\n // If timestamp is in the future, maximum score\n if (ageMs <= 0) return 1;\n\n const ageHours = ageMs / (1000 * 60 * 60);\n\n // Exponential decay: exp(-age * ln(2) / halfLife)\n return Math.exp(-ageHours * Math.LN2 / halfLifeHours);\n}\n\n/**\n * Normalize a raw FTS5 rank into 0-1 range.\n * FTS5 rank is negative: more negative = more relevant.\n * Min-max normalization relative to all ranks in the batch.\n *\n * @param rank - Raw FTS5 rank (negative)\n * @param allRanks - All ranks in the batch for normalization\n * @returns Score 0-1 (1 = most relevant)\n */\nexport function normalizeFTS5Rank(rank: number, allRanks: number[]): number {\n if (allRanks.length === 0) return 0;\n if (allRanks.length === 1) return 1; // Single result: maximum relevance\n\n const minRank = Math.min(...allRanks); // Most negative = best\n const maxRank = Math.max(...allRanks); // Least negative = worst\n\n // If all equal, return 1\n if (minRank === maxRank) return 1;\n\n // Invert: most negative becomes 1, least negative becomes 0\n return (maxRank - rank) / (maxRank - minRank);\n}\n\n/**\n * Binary score for project match.\n *\n * @param itemProject - Project of the item\n * @param targetProject - Target project (e.g. current project)\n * @returns 1 if they match, 0 otherwise\n */\nexport function projectMatchScore(itemProject: string, targetProject: string): number {\n if (!itemProject || !targetProject) return 0;\n return itemProject.toLowerCase() === targetProject.toLowerCase() ? 1 : 0;\n}\n\n/**\n * Calculate weighted composite score combining the 4 signals.\n *\n * @param signals - Values of the 4 signals (each 0-1)\n * @param weights - Weights for each signal\n * @returns Composite score 0-1\n */\nexport function computeCompositeScore(\n signals: {\n semantic: number;\n fts5: number;\n recency: number;\n projectMatch: number;\n },\n weights: ScoringWeights\n): number {\n return (\n signals.semantic * weights.semantic +\n signals.fts5 * weights.fts5 +\n signals.recency * weights.recency +\n signals.projectMatch * weights.projectMatch\n );\n}\n\n/**\n * Recency score based on last access (search that found the observation).\n * Uses shorter half-life than recencyScore() because access is more volatile.\n *\n * If the observation was never accessed, returns 0 (maximum penalty).\n *\n * @param lastAccessedEpoch - Last access timestamp in milliseconds (null if never accessed)\n * @param halfLifeHours - Half-life in hours (default: 48 = 2 days)\n * @returns Score 0-1 (1 = recently accessed)\n */\nexport function accessRecencyScore(lastAccessedEpoch: number | null, halfLifeHours: number = 48): number {\n if (!lastAccessedEpoch || lastAccessedEpoch <= 0) return 0;\n\n const nowMs = Date.now();\n const ageMs = nowMs - lastAccessedEpoch;\n\n // If timestamp is in the future, maximum score\n if (ageMs <= 0) return 1;\n\n const ageHours = ageMs / (1000 * 60 * 60);\n return Math.exp(-ageHours * Math.LN2 / halfLifeHours);\n}\n\n/**\n * Penalty for stale observations (files modified after the observation).\n * Returns a multiplier: 1.0 if fresh, 0.5 if stale.\n * Does not remove the observation from ranking but penalizes it significantly.\n *\n * @param isStale - Stale flag (0 = fresh, 1 = stale)\n * @returns Multiplier 0.5-1.0\n */\nexport function stalenessPenalty(isStale: number): number {\n return isStale === 1 ? 0.5 : 1.0;\n}\n\n/**\n * Multiplicative boost for structured knowledge types.\n * Constraint and decision weigh more because they represent critical rules and choices.\n * Non-knowledge types stay at 1.0 (no boost).\n */\nexport const KNOWLEDGE_TYPE_BOOST: Record<string, number> = {\n constraint: 1.30,\n decision: 1.25,\n heuristic: 1.15,\n rejected: 1.10\n};\n\n/**\n * Returns the boost multiplier for the observation type.\n * Knowledge types get a boost, all others stay at 1.0.\n *\n * @param type - Observation type\n * @returns Multiplier >= 1.0\n */\nexport function knowledgeTypeBoost(type: string): number {\n return KNOWLEDGE_TYPE_BOOST[type] ?? 1.0;\n}\n\n/**\n * Approximate token estimation from a string.\n * Uses the rule of thumb: 1 token \u2248 4 characters.\n */\nexport function estimateTokens(text: string): number {\n if (!text) return 0;\n return Math.ceil(text.length / 4);\n}\n", "/**\n * Hybrid search: combines local vector search (SQLite BLOB) with keyword search (FTS5)\n *\n * 4-signal scoring:\n * - semantic: cosine similarity from embedding\n * - fts5: normalized FTS5 rank\n * - recency: exponential decay\n * - projectMatch: project match\n *\n * If the embedding service is not available, falls back to FTS5 only.\n */\n\nimport { getEmbeddingService } from './EmbeddingService.js';\nimport { getVectorSearch } from './VectorSearch.js';\nimport {\n recencyScore,\n normalizeFTS5Rank,\n projectMatchScore,\n computeCompositeScore,\n knowledgeTypeBoost,\n SEARCH_WEIGHTS\n} from './ScoringEngine.js';\nimport type { Database } from 'bun:sqlite';\nimport type { ScoringWeights } from '../../types/worker-types.js';\nimport { logger } from '../../utils/logger.js';\n\nexport interface SearchResult {\n id: string;\n title: string;\n content: string;\n type: string;\n project: string;\n created_at: string;\n created_at_epoch: number;\n score: number;\n source: 'vector' | 'keyword' | 'hybrid';\n signals: {\n semantic: number;\n fts5: number;\n recency: number;\n projectMatch: number;\n };\n}\n\nexport class HybridSearch {\n private embeddingInitialized = false;\n\n /**\n * Initialize the embedding service (lazy, non-blocking)\n */\n async initialize(): Promise<void> {\n try {\n const embeddingService = getEmbeddingService();\n await embeddingService.initialize();\n this.embeddingInitialized = embeddingService.isAvailable();\n logger.info('SEARCH', `HybridSearch initialized (embedding: ${this.embeddingInitialized ? 'active' : 'disabled'})`);\n } catch (error) {\n logger.warn('SEARCH', 'Embedding initialization failed, using only FTS5', {}, error as Error);\n this.embeddingInitialized = false;\n }\n }\n\n /**\n * Hybrid search with 4-signal scoring\n */\n async search(\n db: Database,\n query: string,\n options: {\n project?: string;\n limit?: number;\n weights?: ScoringWeights;\n } = {}\n ): Promise<SearchResult[]> {\n const limit = options.limit || 10;\n const weights = options.weights || SEARCH_WEIGHTS;\n const targetProject = options.project || '';\n\n // Collect raw results from both sources\n const rawItems = new Map<string, {\n id: string;\n title: string;\n content: string;\n type: string;\n project: string;\n created_at: string;\n created_at_epoch: number;\n semanticScore: number;\n fts5Rank: number | null; // raw rank, to be normalized later\n source: 'vector' | 'keyword';\n }>();\n\n // Vector search (if embedding available)\n if (this.embeddingInitialized) {\n try {\n const embeddingService = getEmbeddingService();\n const queryEmbedding = await embeddingService.embed(query);\n\n if (queryEmbedding) {\n const vectorSearch = getVectorSearch();\n const vectorResults = await vectorSearch.search(db, queryEmbedding, {\n project: options.project,\n limit: limit * 2, // Fetch more results for ranking\n threshold: 0.3\n });\n\n for (const hit of vectorResults) {\n rawItems.set(String(hit.observationId), {\n id: String(hit.observationId),\n title: hit.title,\n content: hit.text || '',\n type: hit.type,\n project: hit.project,\n created_at: hit.created_at,\n created_at_epoch: hit.created_at_epoch,\n semanticScore: hit.similarity,\n fts5Rank: null,\n source: 'vector'\n });\n }\n\n logger.debug('SEARCH', `Vector search: ${vectorResults.length} results`);\n }\n } catch (error) {\n logger.warn('SEARCH', 'Vector search failed, using only keyword', {}, error as Error);\n }\n }\n\n // Keyword search FTS5 with rank (always active)\n try {\n const { searchObservationsFTSWithRank } = await import('../sqlite/Search.js');\n const keywordResults = searchObservationsFTSWithRank(db, query, {\n project: options.project,\n limit: limit * 2\n });\n\n for (const obs of keywordResults) {\n const id = String(obs.id);\n const existing = rawItems.get(id);\n\n if (existing) {\n // Present in both sources: add FTS5 rank\n existing.fts5Rank = obs.fts5_rank;\n existing.source = 'vector'; // Keep vector as primary source\n } else {\n rawItems.set(id, {\n id,\n title: obs.title,\n content: obs.text || obs.narrative || '',\n type: obs.type,\n project: obs.project,\n created_at: obs.created_at,\n created_at_epoch: obs.created_at_epoch,\n semanticScore: 0,\n fts5Rank: obs.fts5_rank,\n source: 'keyword'\n });\n }\n }\n\n logger.debug('SEARCH', `Keyword search: ${keywordResults.length} results`);\n } catch (error) {\n logger.error('SEARCH', 'Keyword search failed', {}, error as Error);\n }\n\n // No results\n if (rawItems.size === 0) return [];\n\n // Normalize FTS5 ranks\n const allFTS5Ranks = Array.from(rawItems.values())\n .filter(item => item.fts5Rank !== null)\n .map(item => item.fts5Rank as number);\n\n // Compute composite score for each item\n const scored: SearchResult[] = [];\n\n for (const item of rawItems.values()) {\n const signals = {\n semantic: item.semanticScore,\n fts5: item.fts5Rank !== null ? normalizeFTS5Rank(item.fts5Rank, allFTS5Ranks) : 0,\n recency: recencyScore(item.created_at_epoch),\n projectMatch: targetProject ? projectMatchScore(item.project, targetProject) : 0\n };\n\n const score = computeCompositeScore(signals, weights);\n\n // Boost for items present in both sources\n const isHybrid = item.semanticScore > 0 && item.fts5Rank !== null;\n const hybridBoost = isHybrid ? 1.15 : 1.0;\n // Boost for knowledge types (constraint, decision, heuristic, rejected)\n const finalScore = Math.min(1, score * hybridBoost * knowledgeTypeBoost(item.type));\n\n scored.push({\n id: item.id,\n title: item.title,\n content: item.content,\n type: item.type,\n project: item.project,\n created_at: item.created_at,\n created_at_epoch: item.created_at_epoch,\n score: finalScore,\n source: isHybrid ? 'hybrid' : item.source,\n signals\n });\n }\n\n // Sort by score descending and limit\n scored.sort((a, b) => b.score - a.score);\n const finalResults = scored.slice(0, limit);\n\n // Access tracking: update last_accessed_epoch for found results (fire-and-forget)\n if (finalResults.length > 0) {\n try {\n const { updateLastAccessed } = await import('../sqlite/Observations.js');\n const ids = finalResults.map(r => parseInt(r.id, 10)).filter(id => id > 0);\n if (ids.length > 0) {\n updateLastAccessed(db, ids);\n }\n } catch {\n // Don't propagate errors \u2014 access tracking is optional\n }\n }\n\n return finalResults;\n }\n}\n\n// Singleton\nlet hybridSearch: HybridSearch | null = null;\n\nexport function getHybridSearch(): HybridSearch {\n if (!hybridSearch) {\n hybridSearch = new HybridSearch();\n }\n return hybridSearch;\n}\n", "/**\n * Shared context for all worker routers.\n * Centralizes database, SSE broadcast, cache, and validation helpers.\n */\n\nimport type { Response } from 'express';\nimport { KiroMemoryDatabase } from './sqlite/Database.js';\nimport { getEmbeddingService } from './search/EmbeddingService.js';\nimport { getVectorSearch } from './search/VectorSearch.js';\nimport { logger } from '../utils/logger.js';\n\n// \u2500\u2500 Shared context type \u2500\u2500\n\nexport interface WorkerContext {\n db: KiroMemoryDatabase;\n broadcast: (event: string, data: any) => void;\n invalidateProjectsCache: () => void;\n generateEmbeddingForObservation: (\n observationId: number,\n title: string,\n content: string | null,\n concepts?: string[]\n ) => Promise<void>;\n}\n\n// \u2500\u2500 SSE Client Management \u2500\u2500\n\nconst MAX_SSE_CLIENTS = 50;\nconst clients: Response[] = [];\n\nexport function getClients(): Response[] {\n return clients;\n}\n\nexport function getMaxSSEClients(): number {\n return MAX_SSE_CLIENTS;\n}\n\nexport function addClient(res: Response): void {\n clients.push(res);\n}\n\nexport function removeClient(res: Response): void {\n const index = clients.indexOf(res);\n if (index > -1) {\n clients.splice(index, 1);\n }\n}\n\n/** Broadcast SSE event to all connected clients */\nexport function broadcast(event: string, data: any): void {\n const message = `event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`;\n clients.forEach(client => {\n try {\n client.write(message);\n } catch (err) {\n logger.warn('WORKER', 'Broadcast failed to client', {}, err as Error);\n }\n });\n}\n\n// \u2500\u2500 Projects cache \u2500\u2500\n\nexport let projectsCache: { data: string[]; ts: number } = { data: [], ts: 0 };\nexport const PROJECTS_CACHE_TTL = 60_000;\n\nexport function invalidateProjectsCache(): void {\n projectsCache.ts = 0;\n}\n\n// \u2500\u2500 Validation helpers (shared across all routers) \u2500\u2500\n\n/** Parse an integer with safe range, returns default if invalid */\nexport function parseIntSafe(value: string | undefined, defaultVal: number, min: number, max: number): number {\n if (!value) return defaultVal;\n const parsed = parseInt(value, 10);\n if (isNaN(parsed) || parsed < min || parsed > max) return defaultVal;\n return parsed;\n}\n\n/** Validate that a project name contains only safe characters */\nexport function isValidProject(project: unknown): project is string {\n return typeof project === 'string'\n && project.length > 0\n && project.length <= 200\n && /^[\\w\\-\\.\\/@ ]+$/.test(project)\n && !project.includes('..');\n}\n\n/** Validate a non-empty string with maximum length */\nexport function isValidString(val: unknown, maxLen: number): val is string {\n return typeof val === 'string' && val.length > 0 && val.length <= maxLen;\n}\n\n// \u2500\u2500 Embedding helper \u2500\u2500\n\n/** Generate embedding for an observation (fire-and-forget) */\nexport async function generateEmbeddingForObservation(\n db: KiroMemoryDatabase,\n observationId: number,\n title: string,\n content: string | null,\n concepts?: string[]\n): Promise<void> {\n try {\n const embeddingService = getEmbeddingService();\n if (!embeddingService.isAvailable()) return;\n\n const parts = [title];\n if (content) parts.push(content);\n if (concepts?.length) parts.push(concepts.join(', '));\n const fullText = parts.join(' ').substring(0, 2000);\n\n const embedding = await embeddingService.embed(fullText);\n if (embedding) {\n const vectorSearch = getVectorSearch();\n await vectorSearch.storeEmbedding(\n db.db,\n observationId,\n embedding,\n embeddingService.getProvider() || 'unknown'\n );\n }\n } catch (error) {\n logger.debug('WORKER', `Embedding generation failed for obs ${observationId}: ${error}`);\n }\n}\n\n// \u2500\u2500 Context factory \u2500\u2500\n\nexport function createWorkerContext(db: KiroMemoryDatabase): WorkerContext {\n return {\n db,\n broadcast,\n invalidateProjectsCache,\n generateEmbeddingForObservation: (id, title, content, concepts) =>\n generateEmbeddingForObservation(db, id, title, content, concepts),\n };\n}\n", "/**\n * Shared types for Kiro Memory Worker Service\n */\n\n// ============================================================================\n// Pagination Types\n// ============================================================================\n\nexport interface PaginatedResult<T> {\n items: T[];\n hasMore: boolean;\n offset: number;\n limit: number;\n}\n\nexport interface PaginationParams {\n offset: number;\n limit: number;\n project?: string;\n}\n\n// ============================================================================\n// Database Record Types\n// ============================================================================\n\nexport interface Observation {\n id: number;\n memory_session_id: string;\n project: string;\n type: string;\n title: string;\n subtitle: string | null;\n text: string | null;\n narrative: string | null;\n facts: string | null;\n concepts: string | null;\n files_read: string | null;\n files_modified: string | null;\n prompt_number: number;\n created_at: string;\n created_at_epoch: number;\n last_accessed_epoch: number | null;\n is_stale: number; // 0 = fresh, 1 = file modified after the observation\n}\n\nexport interface Summary {\n id: number;\n session_id: string;\n project: string;\n request: string | null;\n investigated: string | null;\n learned: string | null;\n completed: string | null;\n next_steps: string | null;\n notes: string | null;\n created_at: string;\n created_at_epoch: number;\n}\n\nexport interface UserPrompt {\n id: number;\n content_session_id: string;\n project: string;\n prompt_number: number;\n prompt_text: string;\n created_at: string;\n created_at_epoch: number;\n}\n\nexport interface DBSession {\n id: number;\n content_session_id: string;\n project: string;\n user_prompt: string;\n memory_session_id: string | null;\n status: 'active' | 'completed' | 'failed';\n started_at: string;\n started_at_epoch: number;\n completed_at: string | null;\n completed_at_epoch: number | null;\n}\n\nexport interface DBCheckpoint {\n id: number;\n session_id: number;\n project: string;\n task: string;\n progress: string | null;\n next_steps: string | null;\n open_questions: string | null;\n relevant_files: string | null;\n context_snapshot: string | null;\n created_at: string;\n created_at_epoch: number;\n}\n\n// ============================================================================\n// Report Types\n// ============================================================================\n\nexport interface ReportData {\n period: {\n start: string;\n end: string;\n days: number;\n label: string;\n };\n overview: {\n observations: number;\n summaries: number;\n sessions: number;\n prompts: number;\n knowledgeCount: number;\n staleCount: number;\n };\n timeline: Array<{ day: string; count: number }>;\n typeDistribution: Array<{ type: string; count: number }>;\n sessionStats: {\n total: number;\n completed: number;\n avgDurationMinutes: number;\n };\n topLearnings: string[];\n completedTasks: string[];\n nextSteps: string[];\n fileHotspots: Array<{ file: string; count: number }>;\n}\n\n// ============================================================================\n// Kiro Integration Types\n// ============================================================================\n\nexport interface KiroMessage {\n role: 'user' | 'assistant' | 'system';\n content: string;\n timestamp?: number;\n}\n\nexport interface KiroSession {\n id: string;\n project: string;\n messages: KiroMessage[];\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface ContextContext {\n project: string;\n relevantObservations: Observation[];\n relevantSummaries: Summary[];\n recentPrompts: UserPrompt[];\n}\n\n// ============================================================================\n// Kiro CLI Hook Types\n// ============================================================================\n\n/**\n * Input JSON received via stdin from Kiro CLI / Claude Code hooks\n *\n * Fields common to all hooks:\n * - session_id, cwd, hook_event_name\n *\n * Hook-specific fields:\n * - UserPromptSubmit: prompt (user text, top-level)\n * - PostToolUse: tool_name, tool_input, tool_response, tool_use_id\n * - Stop: stop_hook_active, transcript_path\n * - SessionStart/agentSpawn: transcript_path\n */\nexport interface KiroHookInput {\n hook_event_name: string;\n session_id?: string;\n cwd: string;\n transcript_path?: string;\n permission_mode?: string;\n\n // UserPromptSubmit: the prompt is top-level, NOT in tool_input\n prompt?: string;\n user_prompt?: string;\n\n // PostToolUse / PreToolUse\n tool_name?: string;\n tool_input?: any;\n tool_response?: any;\n tool_use_id?: string;\n\n // Stop\n stop_hook_active?: boolean;\n\n // Catch-all for fields not yet mapped\n [key: string]: any;\n}\n\n// ============================================================================\n// Search Types\n// ============================================================================\n\nexport interface SearchFilters {\n project?: string;\n type?: string;\n dateStart?: number;\n dateEnd?: number;\n limit?: number;\n}\n\nexport interface SearchResult {\n observations: Observation[];\n summaries: Summary[];\n total: number;\n}\n\nexport interface TimelineEntry {\n id: number;\n type: 'observation' | 'summary' | 'session';\n title: string;\n content: string | null;\n project: string;\n created_at: string;\n created_at_epoch: number;\n}\n\n// ============================================================================\n// Smart Ranking Types (Phase 2B)\n// ============================================================================\n\n/** Weights for the 4 scoring signals */\nexport interface ScoringWeights {\n semantic: number;\n fts5: number;\n recency: number;\n projectMatch: number;\n}\n\n/** Item with composite score and individual signals */\nexport interface ScoredItem {\n id: number;\n title: string;\n content: string;\n type: string;\n project: string;\n created_at: string;\n created_at_epoch: number;\n score: number;\n signals: {\n semantic: number;\n fts5: number;\n recency: number;\n projectMatch: number;\n };\n}\n\n/** Smart context with token budget */\nexport interface SmartContext {\n project: string;\n items: ScoredItem[];\n summaries: Summary[];\n tokenBudget: number;\n tokensUsed: number;\n}\n\n// ============================================================================\n// Structured Knowledge Types (Phase 5A)\n// ============================================================================\n\n/** Structured knowledge types */\nexport type KnowledgeType = 'constraint' | 'decision' | 'heuristic' | 'rejected';\n\n/** Constant for runtime validation */\nexport const KNOWLEDGE_TYPES: KnowledgeType[] = ['constraint', 'decision', 'heuristic', 'rejected'];\n\n/** Metadata for constraints (hard/soft rules) */\nexport interface ConstraintMeta {\n knowledgeType: 'constraint';\n severity: 'hard' | 'soft';\n reason?: string;\n}\n\n/** Metadata for architectural decisions */\nexport interface DecisionMeta {\n knowledgeType: 'decision';\n alternatives?: string[];\n reason?: string;\n}\n\n/** Metadata for preferences/heuristics */\nexport interface HeuristicMeta {\n knowledgeType: 'heuristic';\n context?: string;\n confidence?: 'high' | 'medium' | 'low';\n}\n\n/** Metadata for rejected solutions */\nexport interface RejectedMeta {\n knowledgeType: 'rejected';\n reason: string;\n alternatives?: string[];\n}\n\n/** Discriminated union for knowledge metadata */\nexport type KnowledgeMetadata = ConstraintMeta | DecisionMeta | HeuristicMeta | RejectedMeta;\n\n/** Input for storing structured knowledge */\nexport interface StoreKnowledgeInput {\n project: string;\n knowledgeType: KnowledgeType;\n title: string;\n content: string;\n concepts?: string[];\n files?: string[];\n /** Type-specific metadata (severity, alternatives, reason, context, confidence) */\n metadata?: Partial<Omit<ConstraintMeta, 'knowledgeType'>> &\n Partial<Omit<DecisionMeta, 'knowledgeType'>> &\n Partial<Omit<HeuristicMeta, 'knowledgeType'>> &\n Partial<Omit<RejectedMeta, 'knowledgeType'>>;\n}\n", "/**\n * Retention \u2014 politiche di pulizia automatica dei dati storici.\n *\n * Espone due funzioni principali:\n * - getRetentionStats: calcola quanti record verrebbero eliminati (dry-run)\n * - applyRetention: elimina effettivamente i record scaduti\n *\n * Regole:\n * - Le observation di tipo knowledge (constraint, decision, heuristic, rejected)\n * vengono gestite con la policy `retention.knowledge.maxAgeDays` separata.\n * - maxAgeDays = 0 significa \"non eliminare mai\" per qualsiasi tipo.\n * - Le observation knowledge con importance >= 4 (campo `facts` JSON) sono\n * sempre esentate, indipendentemente dalla configurazione.\n */\n\nimport { Database } from 'bun:sqlite';\nimport { KNOWLEDGE_TYPES } from '../../types/worker-types.js';\n\n// \u2500\u2500 Tipi pubblici \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Configurazione retention letta dal file config.json */\nexport interface RetentionConfig {\n /** Et\u00E0 massima in giorni per le observation generiche (0 = mai eliminare) */\n observationsMaxAgeDays: number;\n /** Et\u00E0 massima in giorni per i summary (0 = mai eliminare) */\n summariesMaxAgeDays: number;\n /** Et\u00E0 massima in giorni per i prompt (0 = mai eliminare) */\n promptsMaxAgeDays: number;\n /** Et\u00E0 massima in giorni per le knowledge (0 = mai eliminare) */\n knowledgeMaxAgeDays: number;\n}\n\n/** Conteggio di record da eliminare per tipo (output dry-run) */\nexport interface RetentionStats {\n observations: number;\n summaries: number;\n prompts: number;\n knowledge: number;\n total: number;\n}\n\n/** Conteggio di record effettivamente eliminati (output apply) */\nexport interface RetentionResult {\n observations: number;\n summaries: number;\n prompts: number;\n knowledge: number;\n total: number;\n executedAt: string;\n}\n\n// \u2500\u2500 Costanti interne \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Tipi observation che rappresentano knowledge strutturata */\nconst KNOWLEDGE_TYPE_LIST = KNOWLEDGE_TYPES as readonly string[];\n\n/** Placeholder SQL per la lista dei knowledge type */\nconst KNOWLEDGE_PLACEHOLDERS = KNOWLEDGE_TYPE_LIST.map(() => '?').join(', ');\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Converte giorni in milliseconds epoch threshold.\n * Ritorna null se maxAgeDays === 0 (nessuna eliminazione).\n */\nfunction toEpochThreshold(maxAgeDays: number): number | null {\n if (maxAgeDays <= 0) return null;\n return Date.now() - maxAgeDays * 86_400_000;\n}\n\n/**\n * Verifica se un record observation (knowledge) ha importance >= 4 nel campo facts.\n * Il campo facts \u00E8 una stringa JSON; se contiene `\"importance\": N` con N >= 4,\n * il record \u00E8 esente dalla retention.\n *\n * Nota: la verifica \u00E8 fatta in SQL con LIKE per evitare deserializzazione\n * su milioni di righe \u2014 sicura perch\u00E9 il formato del JSON \u00E8 controllato.\n */\nfunction buildKnowledgeImportanceExemptionClause(): string {\n // Esenzione: facts contiene \"importance\": 4 o 5 (o pi\u00F9 cifre >= 4)\n // Il pattern \u00E8 conservativo: esclude solo valori >= 4 scritti come intero\n return `AND NOT (\n facts IS NOT NULL AND (\n facts LIKE '%\"importance\":4%'\n OR facts LIKE '%\"importance\": 4%'\n OR facts LIKE '%\"importance\":5%'\n OR facts LIKE '%\"importance\": 5%'\n )\n )`;\n}\n\n// \u2500\u2500 Funzioni pubbliche \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Calcola quanti record verrebbero eliminati con la configurazione fornita.\n * Non modifica il database \u2014 usata per dry-run e preview API.\n *\n * @param db - Istanza SQLite (better-sqlite3 o bun:sqlite)\n * @param config - Configurazione retention\n * @returns Conteggio per tipo e totale\n */\nexport function getRetentionStats(db: Database, config: RetentionConfig): RetentionStats {\n const obsThreshold = toEpochThreshold(config.observationsMaxAgeDays);\n const sumThreshold = toEpochThreshold(config.summariesMaxAgeDays);\n const promptThreshold = toEpochThreshold(config.promptsMaxAgeDays);\n const knowledgeThreshold = toEpochThreshold(config.knowledgeMaxAgeDays);\n\n const importanceExemption = buildKnowledgeImportanceExemptionClause();\n\n // Observation generiche (escluse le knowledge)\n let observations = 0;\n if (obsThreshold !== null) {\n const row = db.query(\n `SELECT COUNT(*) as c FROM observations\n WHERE created_at_epoch < ?\n AND type NOT IN (${KNOWLEDGE_PLACEHOLDERS})`\n ).get(obsThreshold, ...KNOWLEDGE_TYPE_LIST) as { c: number };\n observations = row?.c ?? 0;\n }\n\n // Summaries\n let summaries = 0;\n if (sumThreshold !== null) {\n const row = db.query(\n 'SELECT COUNT(*) as c FROM summaries WHERE created_at_epoch < ?'\n ).get(sumThreshold) as { c: number };\n summaries = row?.c ?? 0;\n }\n\n // Prompts\n let prompts = 0;\n if (promptThreshold !== null) {\n const row = db.query(\n 'SELECT COUNT(*) as c FROM prompts WHERE created_at_epoch < ?'\n ).get(promptThreshold) as { c: number };\n prompts = row?.c ?? 0;\n }\n\n // Knowledge (con esenzione importance >= 4)\n let knowledge = 0;\n if (knowledgeThreshold !== null) {\n const row = db.query(\n `SELECT COUNT(*) as c FROM observations\n WHERE created_at_epoch < ?\n AND type IN (${KNOWLEDGE_PLACEHOLDERS})\n ${importanceExemption}`\n ).get(knowledgeThreshold, ...KNOWLEDGE_TYPE_LIST) as { c: number };\n knowledge = row?.c ?? 0;\n }\n\n const total = observations + summaries + prompts + knowledge;\n return { observations, summaries, prompts, knowledge, total };\n}\n\n/**\n * Conta i record che corrispondono a una query di retention.\n * Helper interno usato sia per dry-run sia per ottenere il conteggio preciso\n * prima di eseguire un DELETE (i trigger FTS5 falserebbero result.changes).\n */\nfunction countRows(db: Database, sql: string, params: unknown[]): number {\n const row = db.query(sql).get(...params) as { c: number } | null;\n return row?.c ?? 0;\n}\n\n/**\n * Elimina i record scaduti secondo la configurazione retention.\n * Usa una transazione atomica per garantire coerenza.\n *\n * Nota implementativa: `result.changes` in bun:sqlite/SQLite include le righe\n * modificate dai trigger (es. FTS5), quindi non riflette il conteggio reale\n * delle righe eliminate dalla tabella principale. Per questo motivo si usa\n * `SELECT COUNT(*)` prima di ogni DELETE per ottenere il valore corretto.\n *\n * Regole:\n * - maxAgeDays = 0 \u2192 tipo saltato completamente\n * - Knowledge con importance >= 4 nel campo facts \u2192 esentate sempre\n * - Prima di eliminare observation, elimina gli embeddings collegati (FK)\n *\n * @param db - Istanza SQLite\n * @param config - Configurazione retention\n * @returns Conteggio record eliminati per tipo e totale\n */\nexport function applyRetention(db: Database, config: RetentionConfig): RetentionResult {\n const obsThreshold = toEpochThreshold(config.observationsMaxAgeDays);\n const sumThreshold = toEpochThreshold(config.summariesMaxAgeDays);\n const promptThreshold = toEpochThreshold(config.promptsMaxAgeDays);\n const knowledgeThreshold = toEpochThreshold(config.knowledgeMaxAgeDays);\n\n const importanceExemption = buildKnowledgeImportanceExemptionClause();\n\n // Esegui tutto in un'unica transazione per atomicit\u00E0\n const deleteAll = db.transaction(() => {\n let observations = 0;\n let summaries = 0;\n let prompts = 0;\n let knowledge = 0;\n\n // 1. Elimina observation generiche (non knowledge) scadute\n if (obsThreshold !== null) {\n const obsParams: unknown[] = [obsThreshold, ...KNOWLEDGE_TYPE_LIST];\n const obsWhere = `WHERE created_at_epoch < ? AND type NOT IN (${KNOWLEDGE_PLACEHOLDERS})`;\n\n // Conta prima del DELETE (result.changes include trigger FTS5 e non \u00E8 affidabile)\n observations = countRows(db,\n `SELECT COUNT(*) as c FROM observations ${obsWhere}`,\n obsParams\n );\n\n if (observations > 0) {\n // Elimina embeddings collegati (FK) prima della observation principale\n db.run(\n `DELETE FROM observation_embeddings\n WHERE observation_id IN (\n SELECT id FROM observations ${obsWhere}\n )`,\n obsParams\n );\n db.run(\n `DELETE FROM observations ${obsWhere}`,\n obsParams\n );\n }\n }\n\n // 2. Elimina summaries scaduti\n if (sumThreshold !== null) {\n summaries = countRows(db,\n 'SELECT COUNT(*) as c FROM summaries WHERE created_at_epoch < ?',\n [sumThreshold]\n );\n if (summaries > 0) {\n db.run('DELETE FROM summaries WHERE created_at_epoch < ?', [sumThreshold]);\n }\n }\n\n // 3. Elimina prompts scaduti\n if (promptThreshold !== null) {\n prompts = countRows(db,\n 'SELECT COUNT(*) as c FROM prompts WHERE created_at_epoch < ?',\n [promptThreshold]\n );\n if (prompts > 0) {\n db.run('DELETE FROM prompts WHERE created_at_epoch < ?', [promptThreshold]);\n }\n }\n\n // 4. Elimina knowledge scadute (con esenzione importance >= 4)\n if (knowledgeThreshold !== null) {\n const kParams: unknown[] = [knowledgeThreshold, ...KNOWLEDGE_TYPE_LIST];\n const kWhere = `WHERE created_at_epoch < ? AND type IN (${KNOWLEDGE_PLACEHOLDERS}) ${importanceExemption}`;\n\n knowledge = countRows(db,\n `SELECT COUNT(*) as c FROM observations ${kWhere}`,\n kParams\n );\n\n if (knowledge > 0) {\n // Elimina embeddings collegati prima\n db.run(\n `DELETE FROM observation_embeddings\n WHERE observation_id IN (\n SELECT id FROM observations ${kWhere}\n )`,\n kParams\n );\n db.run(\n `DELETE FROM observations ${kWhere}`,\n kParams\n );\n }\n }\n\n return { observations, summaries, prompts, knowledge };\n });\n\n const counts = deleteAll();\n const total = counts.observations + counts.summaries + counts.prompts + counts.knowledge;\n\n return {\n ...counts,\n total,\n executedAt: new Date().toISOString(),\n };\n}\n\n/**\n * Costruisce un RetentionConfig leggendo i valori dalla configurazione applicativa.\n * Usa i valori di default se non presenti.\n *\n * @param config - Record di configurazione letto da readConfig() o listConfig()\n */\nexport function buildRetentionConfig(\n config: Record<string, string | number | boolean | null>\n): RetentionConfig {\n function getNum(key: string, fallback: number): number {\n const v = config[key];\n if (v === null || v === undefined) return fallback;\n const n = Number(v);\n return isNaN(n) ? fallback : n;\n }\n\n return {\n observationsMaxAgeDays: getNum('retention.observations.maxAgeDays', 90),\n summariesMaxAgeDays: getNum('retention.summaries.maxAgeDays', 365),\n promptsMaxAgeDays: getNum('retention.prompts.maxAgeDays', 30),\n knowledgeMaxAgeDays: getNum('retention.knowledge.maxAgeDays', 0),\n };\n}\n", "/**\n * Modulo Backup per Kiro Memory.\n *\n * Fornisce funzioni per creare, elencare, ripristinare e ruotare i backup\n * del database SQLite. Utilizza copia diretta del file per compatibilit\u00E0\n * con WAL mode senza dipendere da API bun:sqlite native.\n *\n * Directory backup: ~/.contextkit/backups/\n * Nome file DB: backup-YYYY-MM-DD-HHmmss.db\n * Nome metadata: backup-YYYY-MM-DD-HHmmss.meta.json\n */\n\nimport {\n existsSync,\n mkdirSync,\n copyFileSync,\n readdirSync,\n statSync,\n unlinkSync,\n readFileSync,\n writeFileSync,\n} from 'fs';\nimport { join, basename } from 'path';\nimport type { Database } from 'bun:sqlite';\nimport { logger } from '../../utils/logger.js';\n\n// \u2500\u2500\u2500 Tipi pubblici \u2500\u2500\u2500\n\n/** Statistiche del database incluse nel metadata del backup */\nexport interface BackupStats {\n /** Numero di observations nel DB al momento del backup */\n observations: number;\n /** Numero di sessioni nel DB al momento del backup */\n sessions: number;\n /** Numero di summary nel DB al momento del backup */\n summaries: number;\n /** Numero di prompts nel DB al momento del backup */\n prompts: number;\n /** Dimensione del file DB in byte */\n dbSizeBytes: number;\n}\n\n/** Metadata associato a ogni backup */\nexport interface BackupMetadata {\n /** Timestamp ISO del backup */\n timestamp: string;\n /** Timestamp epoch Unix (ms) */\n timestampEpoch: number;\n /** Versione schema DB (dalla tabella schema_versions) */\n schemaVersion: number;\n /** Statistiche del DB al momento del backup */\n stats: BackupStats;\n /** Percorso assoluto del file DB originale */\n sourcePath: string;\n /** Nome base del file backup (senza directory) */\n filename: string;\n}\n\n/** Voce nell'elenco backup (metadata + percorso assoluto) */\nexport interface BackupEntry {\n /** Percorso assoluto del file .db */\n filePath: string;\n /** Percorso assoluto del file .meta.json */\n metaPath: string;\n /** Metadata del backup */\n metadata: BackupMetadata;\n}\n\n// \u2500\u2500\u2500 Funzioni interne \u2500\u2500\u2500\n\n/**\n * Genera il timestamp formattato per il nome del file.\n * Formato: YYYY-MM-DD-HHmmss-mmm (inclusi millisecondi per unicit\u00E0)\n */\nfunction formatTimestamp(date: Date): string {\n const pad = (n: number, len = 2): string => String(n).padStart(len, '0');\n const year = date.getFullYear();\n const month = pad(date.getMonth() + 1);\n const day = pad(date.getDate());\n const hours = pad(date.getHours());\n const mins = pad(date.getMinutes());\n const secs = pad(date.getSeconds());\n const ms = pad(date.getMilliseconds(), 3);\n return `${year}-${month}-${day}-${hours}${mins}${secs}-${ms}`;\n}\n\n/**\n * Raccoglie le statistiche dal database tramite COUNT(*) sulle tabelle principali.\n * Se una tabella non esiste, il conteggio \u00E8 0.\n */\nfunction collectStats(db: Database, dbPath: string): BackupStats {\n const countTable = (table: string): number => {\n try {\n const row = db.query(`SELECT COUNT(*) as c FROM ${table}`).get() as { c: number } | null;\n return row?.c ?? 0;\n } catch {\n return 0;\n }\n };\n\n const dbSizeBytes = existsSync(dbPath) ? statSync(dbPath).size : 0;\n\n return {\n observations: countTable('observations'),\n sessions: countTable('sessions'),\n summaries: countTable('summaries'),\n prompts: countTable('prompts'),\n dbSizeBytes,\n };\n}\n\n/**\n * Legge la versione dello schema dal database.\n * Ritorna 0 se la tabella non esiste.\n */\nfunction getSchemaVersion(db: Database): number {\n try {\n const row = db.query('SELECT MAX(version) as v FROM schema_versions').get() as { v: number } | null;\n return row?.v ?? 0;\n } catch {\n return 0;\n }\n}\n\n// \u2500\u2500\u2500 API pubblica \u2500\u2500\u2500\n\n/**\n * Crea un backup del database SQLite.\n *\n * Copia il file .db e, se presenti in modalit\u00E0 WAL, anche -wal e -shm.\n * Genera il file metadata JSON con versione schema e statistiche.\n *\n * @param dbPath - Percorso assoluto del file DB sorgente\n * @param backupDir - Directory dove salvare i backup\n * @param db - Istanza Database per raccogliere le statistiche\n * @returns BackupEntry con metadata e percorsi dei file creati\n */\nexport function createBackup(\n dbPath: string,\n backupDir: string,\n db: Database\n): BackupEntry {\n // Assicura che la directory esista\n mkdirSync(backupDir, { recursive: true });\n\n const now = new Date();\n const ts = formatTimestamp(now);\n const filename = `backup-${ts}.db`;\n const destPath = join(backupDir, filename);\n const metaFilename = `backup-${ts}.meta.json`;\n const metaPath = join(backupDir, metaFilename);\n\n // Copia il file principale\n if (!existsSync(dbPath)) {\n throw new Error(`Database non trovato: ${dbPath}`);\n }\n copyFileSync(dbPath, destPath);\n logger.info('BACKUP', `File DB copiato: ${dbPath} \u2192 ${destPath}`);\n\n // Copia WAL e SHM se esistono (modalit\u00E0 WAL di SQLite)\n const walPath = `${dbPath}-wal`;\n const shmPath = `${dbPath}-shm`;\n if (existsSync(walPath)) {\n copyFileSync(walPath, `${destPath}-wal`);\n logger.debug('BACKUP', 'File WAL copiato');\n }\n if (existsSync(shmPath)) {\n copyFileSync(shmPath, `${destPath}-shm`);\n logger.debug('BACKUP', 'File SHM copiato');\n }\n\n // Raccoglie statistiche e versione schema\n const stats = collectStats(db, dbPath);\n const schemaVersion = getSchemaVersion(db);\n\n const metadata: BackupMetadata = {\n timestamp: now.toISOString(),\n timestampEpoch: now.getTime(),\n schemaVersion,\n stats,\n sourcePath: dbPath,\n filename,\n };\n\n // Scrive il file metadata\n writeFileSync(metaPath, JSON.stringify(metadata, null, 2), 'utf8');\n logger.info('BACKUP', `Metadata scritto: ${metaPath}`);\n\n return {\n filePath: destPath,\n metaPath,\n metadata,\n };\n}\n\n/**\n * Elenca i backup presenti nella directory, ordinati dal pi\u00F9 recente al pi\u00F9 vecchio.\n *\n * Legge i file .meta.json per ottenere i dati strutturati.\n * I backup senza metadata vengono ignorati con un warning.\n *\n * @param backupDir - Directory dove cercare i backup\n * @returns Array di BackupEntry ordinate per timestamp DESC\n */\nexport function listBackups(backupDir: string): BackupEntry[] {\n if (!existsSync(backupDir)) {\n return [];\n }\n\n const entries: BackupEntry[] = [];\n\n let files: string[];\n try {\n files = readdirSync(backupDir);\n } catch (err) {\n logger.warn('BACKUP', `Impossibile leggere la directory backup: ${backupDir}`, {}, err as Error);\n return [];\n }\n\n // Trova tutti i file .meta.json dei backup\n const metaFiles = files.filter(f => f.startsWith('backup-') && f.endsWith('.meta.json'));\n\n for (const metaFile of metaFiles) {\n const metaPath = join(backupDir, metaFile);\n // Deriva il nome del file DB sostituendo .meta.json con .db\n // Esempio: backup-2026-02-27-150000.meta.json \u2192 backup-2026-02-27-150000.db\n const dbFilename = metaFile.replace(/\\.meta\\.json$/, '.db');\n const filePath = join(backupDir, dbFilename);\n\n // Legge il metadata\n let metadata: BackupMetadata;\n try {\n const raw = readFileSync(metaPath, 'utf8');\n metadata = JSON.parse(raw) as BackupMetadata;\n } catch (err) {\n logger.warn('BACKUP', `Metadata non leggibile: ${metaPath}`, {}, err as Error);\n continue;\n }\n\n // Verifica che il file DB esista\n if (!existsSync(filePath)) {\n logger.warn('BACKUP', `File backup mancante per metadata: ${filePath}`);\n continue;\n }\n\n entries.push({ filePath, metaPath, metadata });\n }\n\n // Ordina dal pi\u00F9 recente al pi\u00F9 vecchio\n entries.sort((a, b) => b.metadata.timestampEpoch - a.metadata.timestampEpoch);\n\n return entries;\n}\n\n/**\n * Ripristina il database da un file di backup.\n *\n * Sovrascrive il file DB corrente con il backup selezionato.\n * Ripristina anche -wal e -shm se presenti nel backup.\n *\n * ATTENZIONE: Il database deve essere chiuso prima di chiamare questa funzione,\n * altrimenti la copia potrebbe corrompere il DB.\n *\n * @param backupFile - Percorso assoluto del file .db da ripristinare\n * @param dbPath - Percorso assoluto del file DB destinazione\n */\nexport function restoreBackup(backupFile: string, dbPath: string): void {\n if (!existsSync(backupFile)) {\n throw new Error(`File backup non trovato: ${backupFile}`);\n }\n\n // Copia il file principale\n copyFileSync(backupFile, dbPath);\n logger.info('BACKUP', `Database ripristinato: ${backupFile} \u2192 ${dbPath}`);\n\n // Ripristina WAL e SHM se presenti nel backup\n const walBackup = `${backupFile}-wal`;\n const shmBackup = `${backupFile}-shm`;\n const walDest = `${dbPath}-wal`;\n const shmDest = `${dbPath}-shm`;\n\n if (existsSync(walBackup)) {\n copyFileSync(walBackup, walDest);\n logger.debug('BACKUP', 'File WAL ripristinato');\n } else if (existsSync(walDest)) {\n // Rimuove il WAL corrente se il backup non ne aveva uno\n unlinkSync(walDest);\n logger.debug('BACKUP', 'File WAL corrente rimosso (non presente nel backup)');\n }\n\n if (existsSync(shmBackup)) {\n copyFileSync(shmBackup, shmDest);\n logger.debug('BACKUP', 'File SHM ripristinato');\n } else if (existsSync(shmDest)) {\n unlinkSync(shmDest);\n logger.debug('BACKUP', 'File SHM corrente rimosso (non presente nel backup)');\n }\n}\n\n/**\n * Ruota i backup eliminando quelli pi\u00F9 vecchi.\n *\n * Mantiene gli ultimi N backup (per timestamp) e rimuove i rimanenti,\n * inclusi i relativi file .meta.json, -wal e -shm.\n *\n * @param backupDir - Directory dove cercare i backup\n * @param maxKeep - Numero massimo di backup da mantenere\n * @returns Numero di backup eliminati\n */\nexport function rotateBackups(backupDir: string, maxKeep: number): number {\n if (maxKeep <= 0) {\n throw new Error(`maxKeep deve essere > 0, ricevuto: ${maxKeep}`);\n }\n\n const entries = listBackups(backupDir);\n\n // Se non supera il limite, nessuna rotazione necessaria\n if (entries.length <= maxKeep) {\n logger.debug('BACKUP', `Rotazione non necessaria: ${entries.length}/${maxKeep} backup presenti`);\n return 0;\n }\n\n // I backup da eliminare sono quelli oltre il limite (listBackups \u00E8 gi\u00E0 ordinata DESC)\n const toDelete = entries.slice(maxKeep);\n let deleted = 0;\n\n for (const entry of toDelete) {\n // Elimina file DB\n try {\n if (existsSync(entry.filePath)) {\n unlinkSync(entry.filePath);\n }\n } catch (err) {\n logger.warn('BACKUP', `Impossibile eliminare: ${entry.filePath}`, {}, err as Error);\n }\n\n // Elimina file WAL e SHM se presenti\n for (const extra of [`${entry.filePath}-wal`, `${entry.filePath}-shm`]) {\n try {\n if (existsSync(extra)) unlinkSync(extra);\n } catch { /* ignora */ }\n }\n\n // Elimina metadata\n try {\n if (existsSync(entry.metaPath)) {\n unlinkSync(entry.metaPath);\n }\n } catch (err) {\n logger.warn('BACKUP', `Impossibile eliminare metadata: ${entry.metaPath}`, {}, err as Error);\n }\n\n logger.info('BACKUP', `Backup rimosso (rotazione): ${basename(entry.filePath)}`);\n deleted++;\n }\n\n logger.info('BACKUP', `Rotazione completata: ${deleted} backup eliminati, ${maxKeep} mantenuti`);\n return deleted;\n}\n", "/**\n * Funzioni CLI testabili estratte dal CLI principale.\n * Questo modulo non ha dipendenze da process.argv e non chiama process.exit,\n * rendendolo adatto per i test unitari.\n */\n\nimport { existsSync, statSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport type { Observation } from '../types/worker-types.js';\n\n// \u2500\u2500\u2500 Tipi pubblici \u2500\u2500\u2500\n\nexport type ExportFormat = 'jsonl' | 'json' | 'md';\n\nexport interface ExportOptions {\n format: ExportFormat;\n project: string;\n output?: string; // percorso file; se omesso, ritorna la stringa\n outputFn?: (line: string) => void; // intercetta l'output per i test\n}\n\nexport interface ImportResult {\n imported: number;\n total: number;\n duplicates: number;\n}\n\nexport interface DoctorFixResult {\n ftsRebuilt: boolean;\n embeddingsRemoved: number;\n vacuumed: boolean;\n messages: string[];\n}\n\nexport interface StatsResult {\n totalObservations: number;\n totalSessions: number;\n totalProjects: number;\n dbSizeBytes: number;\n mostActiveProject: string | null;\n embeddingCoverage: number;\n}\n\nexport interface ConfigValue {\n key: string;\n value: string | number | boolean | null;\n}\n\n// \u2500\u2500\u2500 Export: generatori di formato \u2500\u2500\u2500\n\n/**\n * Converte un'observation in una riga JSONL.\n * Una riga per observation \u2014 compatibile con stream e import successivo.\n */\nexport function observationToJsonl(obs: Observation): string {\n return JSON.stringify(obs);\n}\n\n/**\n * Converte un array di observations in un blocco JSONL (una riga per obs).\n */\nexport function generateJsonlOutput(observations: Observation[]): string {\n return observations.map(observationToJsonl).join('\\n');\n}\n\n/**\n * Converte un array di observations in JSON compatto (array completo).\n */\nexport function generateJsonOutput(observations: Observation[]): string {\n return JSON.stringify(observations, null, 2);\n}\n\n/**\n * Converte un'observation in markdown.\n * Formato: ## titolo + metadati + contenuto.\n */\nexport function observationToMarkdown(obs: Observation): string {\n const date = new Date(obs.created_at).toLocaleDateString('it-IT', {\n year: 'numeric', month: 'long', day: 'numeric'\n });\n\n const lines: string[] = [\n `## ${obs.title}`,\n '',\n `- **Tipo:** ${obs.type}`,\n `- **Progetto:** ${obs.project}`,\n `- **Data:** ${date}`,\n ];\n\n if (obs.subtitle) lines.push(`- **Sottotitolo:** ${obs.subtitle}`);\n if (obs.files_modified) lines.push(`- **File modificati:** ${obs.files_modified}`);\n if (obs.files_read) lines.push(`- **File letti:** ${obs.files_read}`);\n\n if (obs.text) {\n lines.push('', '### Contenuto', '', obs.text);\n }\n\n if (obs.narrative) {\n lines.push('', '### Narrativa', '', obs.narrative);\n }\n\n if (obs.facts) {\n lines.push('', '### Fatti', '', obs.facts);\n }\n\n lines.push('');\n return lines.join('\\n');\n}\n\n/**\n * Converte un array di observations in markdown con separatori.\n */\nexport function generateMarkdownOutput(observations: Observation[]): string {\n if (observations.length === 0) return '# Nessuna observation trovata\\n';\n\n const header = [\n '# Kiro Memory \u2014 Export Observations',\n '',\n `> Progetto: ${observations[0].project} | Totale: ${observations.length}`,\n '',\n '---',\n '',\n ].join('\\n');\n\n return header + observations.map(observationToMarkdown).join('\\n---\\n\\n');\n}\n\n/**\n * Seleziona la funzione di generazione in base al formato.\n */\nexport function generateExportOutput(observations: Observation[], format: ExportFormat): string {\n switch (format) {\n case 'jsonl':\n return generateJsonlOutput(observations);\n case 'json':\n return generateJsonOutput(observations);\n case 'md':\n return generateMarkdownOutput(observations);\n }\n}\n\n// \u2500\u2500\u2500 Import: validazione e parsing \u2500\u2500\u2500\n\n/**\n * Interfaccia minima per un record importabile da JSONL.\n * I campi obbligatori sono project, type e title.\n */\nexport interface ImportRecord {\n project: string;\n type: string;\n title: string;\n memory_session_id?: string;\n subtitle?: string | null;\n text?: string | null;\n narrative?: string | null;\n facts?: string | null;\n concepts?: string | null;\n files_read?: string | null;\n files_modified?: string | null;\n prompt_number?: number;\n content_hash?: string | null;\n discovery_tokens?: number;\n}\n\n/**\n * Valida un record importato da JSONL.\n * Ritorna null se il record \u00E8 valido, altrimenti la descrizione dell'errore.\n */\nexport function validateImportRecord(raw: unknown): string | null {\n if (!raw || typeof raw !== 'object') {\n return 'Record non \u00E8 un oggetto JSON valido';\n }\n\n const rec = raw as Record<string, unknown>;\n\n if (!rec.project || typeof rec.project !== 'string' || rec.project.trim() === '') {\n return 'Campo \"project\" obbligatorio (stringa non vuota)';\n }\n if (!rec.type || typeof rec.type !== 'string' || rec.type.trim() === '') {\n return 'Campo \"type\" obbligatorio (stringa non vuota)';\n }\n if (!rec.title || typeof rec.title !== 'string' || rec.title.trim() === '') {\n return 'Campo \"title\" obbligatorio (stringa non vuota)';\n }\n\n // Lunghezze massime di sicurezza\n if ((rec.project as string).length > 200) return '\"project\" troppo lungo (max 200 caratteri)';\n if ((rec.type as string).length > 100) return '\"type\" troppo lungo (max 100 caratteri)';\n if ((rec.title as string).length > 500) return '\"title\" troppo lungo (max 500 caratteri)';\n\n // Campi opzionali: se presenti, devono essere stringa o null\n for (const field of ['subtitle', 'text', 'narrative', 'facts', 'concepts', 'files_read', 'files_modified', 'content_hash']) {\n const val = rec[field];\n if (val !== undefined && val !== null && typeof val !== 'string') {\n return `Campo \"${field}\" deve essere stringa o null`;\n }\n }\n\n return null; // record valido\n}\n\n/**\n * Analizza un file JSONL e ritorna array di record con errori per riga.\n * Filtra le righe vuote e i commenti (#).\n */\nexport function parseJsonlFile(content: string): Array<{ line: number; record?: ImportRecord; error?: string }> {\n const lines = content.split('\\n');\n const results: Array<{ line: number; record?: ImportRecord; error?: string }> = [];\n\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i].trim();\n\n // Salta righe vuote e commenti\n if (!raw || raw.startsWith('#')) continue;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n results.push({ line: i + 1, error: `JSON non valido: ${raw.substring(0, 50)}` });\n continue;\n }\n\n const validationError = validateImportRecord(parsed);\n if (validationError) {\n results.push({ line: i + 1, error: validationError });\n continue;\n }\n\n results.push({ line: i + 1, record: parsed as ImportRecord });\n }\n\n return results;\n}\n\n// \u2500\u2500\u2500 Config: gestione ~/.contextkit/config.json \u2500\u2500\u2500\n\n/** Percorso di default del file di configurazione */\nexport function getConfigPath(): string {\n const dataDir = process.env.KIRO_MEMORY_DATA_DIR\n || process.env.CONTEXTKIT_DATA_DIR\n || join(homedir(), '.contextkit');\n return join(dataDir, 'config.json');\n}\n\n/** Valori di configurazione predefiniti */\nexport const CONFIG_DEFAULTS: Record<string, string | number | boolean> = {\n 'worker.port': 3001,\n 'worker.host': '127.0.0.1',\n 'log.level': 'INFO',\n 'search.limit': 20,\n 'embeddings.enabled': false,\n 'decay.staleThresholdDays': 30,\n // Politiche di retention: et\u00E0 massima in giorni (0 = mai eliminare)\n 'retention.observations.maxAgeDays': 90,\n 'retention.summaries.maxAgeDays': 365,\n 'retention.prompts.maxAgeDays': 30,\n 'retention.knowledge.maxAgeDays': 0,\n // Cleanup automatico schedulato\n 'retention.autoCleanupEnabled': true,\n 'retention.autoCleanupIntervalHours': 24,\n // Backup automatico schedulato\n 'backup.enabled': true,\n 'backup.intervalHours': 24,\n 'backup.maxKeep': 7,\n 'backup.compress': false,\n};\n\n/**\n * Legge la configurazione da file.\n * Ritorna oggetto vuoto se il file non esiste.\n */\nexport function readConfig(configPath?: string): Record<string, string | number | boolean | null> {\n const path = configPath || getConfigPath();\n\n if (!existsSync(path)) return {};\n\n try {\n const raw = readFileSync(path, 'utf8');\n const parsed = JSON.parse(raw);\n if (typeof parsed === 'object' && parsed !== null) return parsed;\n return {};\n } catch {\n return {};\n }\n}\n\n/**\n * Scrive la configurazione su file.\n * Crea la directory se non esiste.\n */\nexport function writeConfig(\n config: Record<string, string | number | boolean | null>,\n configPath?: string\n): void {\n const path = configPath || getConfigPath();\n const dir = path.substring(0, path.lastIndexOf('/'));\n\n mkdirSync(dir, { recursive: true });\n writeFileSync(path, JSON.stringify(config, null, 2), 'utf8');\n}\n\n/**\n * Legge un singolo valore dalla configurazione.\n * Fallback ai valori di default se non trovato.\n */\nexport function getConfigValue(\n key: string,\n configPath?: string\n): string | number | boolean | null {\n const config = readConfig(configPath);\n\n if (key in config) return config[key];\n if (key in CONFIG_DEFAULTS) return CONFIG_DEFAULTS[key];\n return null;\n}\n\n/**\n * Imposta un singolo valore nella configurazione.\n * Converte automaticamente il tipo (numero, booleano, stringa).\n */\nexport function setConfigValue(\n key: string,\n rawValue: string,\n configPath?: string\n): string | number | boolean {\n const config = readConfig(configPath);\n\n // Prova a convertire nel tipo appropriato\n let value: string | number | boolean = rawValue;\n\n if (rawValue === 'true') value = true;\n else if (rawValue === 'false') value = false;\n else {\n const num = Number(rawValue);\n if (!isNaN(num) && rawValue.trim() !== '') value = num;\n }\n\n config[key] = value;\n writeConfig(config, configPath);\n return value;\n}\n\n/**\n * Lista tutte le chiavi di configurazione (merge default + file).\n */\nexport function listConfig(configPath?: string): Record<string, string | number | boolean | null> {\n const config = readConfig(configPath);\n\n // Merge: default sovrascritta da config file\n const merged: Record<string, string | number | boolean | null> = { ...CONFIG_DEFAULTS };\n for (const [k, v] of Object.entries(config)) {\n merged[k] = v;\n }\n\n return merged;\n}\n\n// \u2500\u2500\u2500 Stats: formato output \u2500\u2500\u2500\n\n/**\n * Formatta il numero di byte in una stringa leggibile (KB, MB, GB).\n */\nexport function formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n if (bytes < 1024 * 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`;\n return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;\n}\n\n/**\n * Recupera la dimensione del file del database.\n * Ritorna 0 se il file non esiste.\n */\nexport function getDbFileSize(dbPath: string): number {\n try {\n if (!existsSync(dbPath)) return 0;\n return statSync(dbPath).size;\n } catch {\n return 0;\n }\n}\n\n/**\n * Formatta il risultato delle statistiche in testo leggibile.\n */\nexport function formatStatsOutput(stats: StatsResult): string {\n const lines: string[] = [\n '',\n '=== Kiro Memory \u2014 Statistiche Database ===',\n '',\n ` Observations totali: ${stats.totalObservations}`,\n ` Sessioni totali: ${stats.totalSessions}`,\n ` Progetti distinti: ${stats.totalProjects}`,\n ` Dimensione DB: ${formatBytes(stats.dbSizeBytes)}`,\n ];\n\n if (stats.mostActiveProject) {\n lines.push(` Progetto piu' attivo: ${stats.mostActiveProject}`);\n }\n\n const coverage = stats.embeddingCoverage;\n const coverageBar = buildProgressBar(coverage, 20);\n lines.push(` Copertura embeddings: ${coverageBar} ${coverage}%`);\n\n lines.push('');\n return lines.join('\\n');\n}\n\n/**\n * Costruisce una barra di avanzamento ASCII.\n */\nexport function buildProgressBar(percent: number, width: number = 20): string {\n const filled = Math.round((percent / 100) * width);\n const empty = width - filled;\n return `[${'#'.repeat(filled)}${'-'.repeat(empty)}]`;\n}\n\n// \u2500\u2500\u2500 Import/Export JSONL: opzioni CLI \u2500\u2500\u2500\n\n/** Opzioni per il comando CLI export JSONL */\nexport interface JsonlExportOptions {\n output?: string; // percorso file output; se omesso, stampa su stdout\n project?: string; // filtra per progetto\n type?: string; // filtra per tipo observation\n from?: string; // data inizio ISO\n to?: string; // data fine ISO\n}\n\n/** Opzioni per il comando CLI import JSONL */\nexport interface JsonlImportOptions {\n dryRun?: boolean; // dry-run: mostra conteggio senza inserire\n}\n\n/**\n * Formatta il risultato di un import JSONL in testo leggibile.\n */\nexport function formatImportResult(result: {\n imported: number;\n skipped: number;\n errors: number;\n total: number;\n dryRun: boolean;\n errorDetails?: Array<{ line: number; error: string }>;\n}): string {\n const prefix = result.dryRun ? '[DRY RUN] ' : '';\n const lines: string[] = [\n '',\n `=== ${prefix}Kiro Memory \u2014 Import JSONL ===`,\n '',\n ` Record totali analizzati: ${result.total}`,\n ` Importati: ${result.imported}`,\n ` Saltati (duplicati): ${result.skipped}`,\n ` Errori di validazione: ${result.errors}`,\n ];\n\n if (result.dryRun) {\n lines.push('');\n lines.push(' (Dry run: nessun dato inserito. Rimuovi --dry-run per applicare.)');\n }\n\n if (result.errorDetails && result.errorDetails.length > 0) {\n lines.push('');\n lines.push(' Errori:');\n for (const err of result.errorDetails.slice(0, 20)) {\n lines.push(` Riga ${err.line}: ${err.error}`);\n }\n if (result.errorDetails.length > 20) {\n lines.push(` ... e altri ${result.errorDetails.length - 20} errori`);\n }\n }\n\n lines.push('');\n return lines.join('\\n');\n}\n\n// \u2500\u2500\u2500 Doctor fix: operazioni riparazione \u2500\u2500\u2500\n\n/**\n * Verifica se un indice FTS5 \u00E8 danneggiato eseguendo una query di integrit\u00E0.\n * Ritorna true se l'indice \u00E8 integro.\n */\nexport function checkFtsIntegrity(db: import('bun:sqlite').Database): boolean {\n try {\n // fts5 integrity check: eseguire insert fittizio su tabella shadow causa errore se corrotta\n db.query(\"INSERT INTO observations_fts(observations_fts) VALUES('integrity-check')\").run();\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Ricostruisce l'indice FTS5 per le observations.\n * Ritorna true se l'operazione \u00E8 riuscita.\n */\nexport function rebuildFtsIndex(db: import('bun:sqlite').Database): boolean {\n try {\n db.run(\"INSERT INTO observations_fts(observations_fts) VALUES('rebuild')\");\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Rimuove gli embeddings orfani (observation_id non pi\u00F9 presente).\n * Ritorna il numero di record rimossi.\n */\nexport function removeOrphanedEmbeddings(db: import('bun:sqlite').Database): number {\n try {\n const result = db.run(\n `DELETE FROM observation_embeddings\n WHERE observation_id NOT IN (SELECT id FROM observations)`\n );\n return Number(result.changes);\n } catch {\n return 0;\n }\n}\n\n/**\n * Esegue VACUUM sul database per recuperare spazio.\n * Ritorna true se l'operazione \u00E8 riuscita.\n */\nexport function vacuumDatabase(db: import('bun:sqlite').Database): boolean {\n try {\n db.run('VACUUM');\n return true;\n } catch {\n return false;\n }\n}\n", "/**\n * PluginLoader \u2014 Discovery automatico e lifecycle management dei plugin.\n *\n * Responsabilit\u00E0:\n * 1. Discovery automatico: scansiona node_modules/kiro-memory-plugin-*\n * 2. Discovery locale: carica plugin da ~/.contextkit/plugins/<nome>/index.js\n * 3. Discovery da configurazione: legge la chiave \"plugins\" in config.json\n * 4. Validazione: verifica che ogni modulo implementi IPlugin\n * 5. Hot reload: supporto per ricaricare un singolo plugin a runtime\n *\n * Integrazione worker:\n * Istanziare dopo la configurazione di Express e chiamare loadAll()\n * prima di avviare il server.\n *\n * Testabilit\u00E0:\n * Il metodo _loadModule(path) \u00E8 separato e pu\u00F2 essere sovrascritta nei test\n * senza toccare la logica di validazione e lifecycle.\n */\n\nimport { existsSync, readdirSync, readFileSync } from 'fs';\nimport { join, resolve } from 'path';\nimport { logger } from '../../utils/logger.js';\nimport { DATA_DIR } from '../../shared/paths.js';\nimport { readConfig } from '../../cli/cli-utils.js';\nimport type { IPlugin } from './types.js';\n\n// \u2500\u2500 Versione del runtime usata per la verifica minKiroVersion \u2500\u2500\nconst KIRO_MEMORY_VERSION = '3.0.0';\n\n/**\n * Interfaccia minima del registry necessaria al Loader.\n * Compatibile con PluginRegistry singleton (issue #29).\n */\nexport interface IPluginRegistry {\n register(plugin: IPlugin): void;\n unregister(name: string): void | Promise<void>;\n /** Restituisce il plugin per nome, o undefined se non registrato */\n get(name: string): IPlugin | undefined;\n /**\n * Alias di get \u2014 compatibilit\u00E0 con MockRegistry dei test e con IPlugin.getPlugin.\n * Il PluginLoader usa internamente get(), questo alias \u00E8 opzionale.\n */\n getPlugin?(name: string): IPlugin | undefined;\n}\n\n// \u2500\u2500 Funzione separata per il dynamic import (testabile via mock) \u2500\u2500\n\n/**\n * Carica dinamicamente un modulo ESM dal percorso indicato.\n * Funzione separata (non metodo di classe) per consentire il mock nei test.\n */\nexport async function loadModuleFromPath(modulePath: string): Promise<unknown> {\n return import(modulePath);\n}\n\n// \u2500\u2500 Configurazione plugin in config.json \u2500\u2500\n\nexport interface PluginConfigEntry {\n /** Nome del plugin (pacchetto npm o nome locale) */\n name: string;\n /** Percorso assoluto del modulo (solo per plugin locali) */\n path?: string;\n /** Se false il plugin viene ignorato; default true */\n enabled?: boolean;\n}\n\n// \u2500\u2500 Risultato di loadAll \u2500\u2500\n\nexport interface LoadAllResult {\n /** Nomi dei plugin caricati con successo */\n loaded: string[];\n /** Plugin non caricati con relativo messaggio di errore */\n failed: Array<{ name: string; error: string }>;\n}\n\n// \u2500\u2500 PluginLoader \u2500\u2500\n\nexport class PluginLoader {\n /**\n * Directory locale dove risiedono i plugin installati dall'utente.\n * Struttura: ~/.contextkit/plugins/<nome>/index.js\n */\n protected readonly localPluginsDir: string;\n\n /**\n * Radice del progetto per la ricerca in node_modules.\n * Default: process.cwd() al momento della costruzione.\n */\n protected readonly projectRoot: string;\n\n /**\n * Mappa dei moduli caricati per supportare il hot reload.\n * Chiave: nome plugin \u2014 Valore: percorso assoluto del modulo.\n */\n protected readonly loadedModulePaths = new Map<string, string>();\n\n constructor(\n protected readonly registry: IPluginRegistry,\n options: {\n localPluginsDir?: string;\n projectRoot?: string;\n } = {}\n ) {\n this.localPluginsDir = options.localPluginsDir ?? join(DATA_DIR, 'plugins');\n this.projectRoot = options.projectRoot ?? process.cwd();\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Hook di estensione per i test\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Carica il modulo grezzo da un percorso.\n * Nei test, sovrascrivere questo metodo per iniettare moduli mock\n * senza toccare il filesystem o il dynamic import.\n */\n protected async _loadModule(modulePath: string): Promise<unknown> {\n return loadModuleFromPath(modulePath);\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Discovery\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Scansiona node_modules alla ricerca di pacchetti con nome\n * che corrisponde al pattern `kiro-memory-plugin-*`.\n *\n * Ritorna i percorsi assoluti dei pacchetti trovati.\n */\n async discoverPlugins(): Promise<string[]> {\n const nodeModulesDir = join(this.projectRoot, 'node_modules');\n\n if (!existsSync(nodeModulesDir)) {\n logger.debug('SYSTEM', `PluginLoader: node_modules non trovato in ${nodeModulesDir}`);\n return [];\n }\n\n const discovered: string[] = [];\n\n try {\n const entries = readdirSync(nodeModulesDir, { withFileTypes: true });\n\n for (const entry of entries) {\n // I pacchetti npm con scope sono in sotto-directory che iniziano con \"@\"\n if (entry.isDirectory() && entry.name.startsWith('@')) {\n const scopeDir = join(nodeModulesDir, entry.name);\n try {\n const scopedEntries = readdirSync(scopeDir, { withFileTypes: true });\n for (const scoped of scopedEntries) {\n if (scoped.isDirectory() && scoped.name.startsWith('kiro-memory-plugin-')) {\n const pkgPath = join(scopeDir, scoped.name);\n if (this.hasValidPackageJson(pkgPath)) {\n discovered.push(pkgPath);\n }\n }\n }\n } catch {\n // Ignora directory scope non leggibili\n }\n continue;\n }\n\n // Pacchetti senza scope\n if (entry.isDirectory() && entry.name.startsWith('kiro-memory-plugin-')) {\n const pkgPath = join(nodeModulesDir, entry.name);\n if (this.hasValidPackageJson(pkgPath)) {\n discovered.push(pkgPath);\n }\n }\n }\n } catch (err) {\n logger.warn('SYSTEM', `PluginLoader: errore durante la scansione di node_modules`, {}, err as Error);\n }\n\n logger.debug('SYSTEM', `PluginLoader: trovati ${discovered.length} plugin in node_modules`);\n return discovered;\n }\n\n /**\n * Verifica che la directory contenga un package.json valido\n * con il campo `main` o `exports` definito.\n */\n private hasValidPackageJson(pkgDir: string): boolean {\n const pkgJsonPath = join(pkgDir, 'package.json');\n if (!existsSync(pkgJsonPath)) return false;\n\n try {\n const raw = readFileSync(pkgJsonPath, 'utf-8');\n const pkg = JSON.parse(raw) as Record<string, unknown>;\n return !!(pkg.main || pkg.exports);\n } catch {\n return false;\n }\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Caricamento da percorso\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Carica un plugin da un percorso assoluto (directory o file .js).\n *\n * Logica di risoluzione:\n * - Se `pluginPath` \u00E8 una directory, cerca `index.js` al suo interno\n * - Altrimenti usa `pluginPath` direttamente come percorso del modulo\n * - Se la directory \u00E8 un pacchetto npm, legge `main` da package.json\n *\n * @throws Error se il percorso non esiste o il modulo non \u00E8 un IPlugin valido\n */\n async loadFromPath(pluginPath: string): Promise<IPlugin> {\n const absolutePath = resolve(pluginPath);\n\n if (!existsSync(absolutePath)) {\n throw new Error(`Percorso plugin non trovato: ${absolutePath}`);\n }\n\n // Risolvi l'entry point corretto\n const entryPoint = this.resolveEntryPoint(absolutePath);\n\n logger.debug('SYSTEM', `PluginLoader: caricamento modulo da ${entryPoint}`);\n\n // Usa il metodo di caricamento moduli (sovrascrivibile nei test)\n const rawModule = await this._loadModule(entryPoint);\n\n const plugin = this.extractPlugin(rawModule);\n this.validatePlugin(plugin);\n\n return plugin;\n }\n\n /**\n * Risolve l'entry point di un plugin dato un percorso.\n * Gestisce: directory con package.json, directory con index.js, file diretto.\n */\n private resolveEntryPoint(absolutePath: string): string {\n const pkgJsonPath = join(absolutePath, 'package.json');\n\n if (existsSync(pkgJsonPath)) {\n // Pacchetto npm: usa `main` da package.json\n try {\n const raw = readFileSync(pkgJsonPath, 'utf-8');\n const pkg = JSON.parse(raw) as Record<string, unknown>;\n if (typeof pkg.main === 'string') {\n return resolve(absolutePath, pkg.main);\n }\n } catch {\n // Ignora errori di parsing e ricade su index.js\n }\n }\n\n // Directory senza package.json: cerca index.js\n const indexPath = join(absolutePath, 'index.js');\n if (existsSync(indexPath)) {\n return indexPath;\n }\n\n // Percorso diretto (file .js o altro)\n return absolutePath;\n }\n\n /**\n * Estrae l'oggetto IPlugin dall'export del modulo caricato.\n * Supporta:\n * - `export default plugin` (oggetto IPlugin)\n * - `export default factory` (funzione che ritorna IPlugin)\n * - `module.exports = plugin` (CJS)\n */\n private extractPlugin(rawModule: unknown): unknown {\n if (!rawModule || typeof rawModule !== 'object') {\n throw new Error('Il modulo non ha un export valido');\n }\n\n const mod = rawModule as Record<string, unknown>;\n\n // ESM default export\n let candidate = mod.default ?? rawModule;\n\n // Se \u00E8 una funzione factory, invocala\n if (typeof candidate === 'function') {\n candidate = (candidate as () => unknown)();\n }\n\n return candidate;\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Caricamento da configurazione\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Legge la chiave `plugins` da config.json e carica ogni plugin configurato.\n * I plugin con `enabled: false` vengono saltati silenziosamente.\n */\n async loadFromConfig(): Promise<void> {\n const config = readConfig();\n const rawPlugins = config['plugins'];\n\n if (!rawPlugins) {\n logger.debug('SYSTEM', 'PluginLoader: nessun plugin configurato in config.json');\n return;\n }\n\n let entries: PluginConfigEntry[];\n\n try {\n entries = (typeof rawPlugins === 'string'\n ? JSON.parse(rawPlugins)\n : rawPlugins) as PluginConfigEntry[];\n\n if (!Array.isArray(entries)) {\n logger.warn('SYSTEM', 'PluginLoader: \"plugins\" in config.json deve essere un array');\n return;\n }\n } catch (err) {\n logger.warn('SYSTEM', 'PluginLoader: errore parsing \"plugins\" in config.json', {}, err as Error);\n return;\n }\n\n for (const entry of entries) {\n // Salta plugin disabilitati esplicitamente\n if (entry.enabled === false) {\n logger.debug('SYSTEM', `PluginLoader: plugin \"${entry.name}\" disabilitato, skip`);\n continue;\n }\n\n try {\n const nameOrPath = entry.path ?? entry.name;\n await this.loadPlugin(nameOrPath, entry.name);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.warn('SYSTEM', `PluginLoader: plugin \"${entry.name}\" non caricato da config: ${msg}`);\n }\n }\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Caricamento singolo plugin\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Carica, valida e registra un singolo plugin.\n *\n * Il metodo utilizza _loadRawPlugin() per ottenere il modulo grezzo,\n * poi applica la validazione e il lifecycle (init + register).\n *\n * @param nameOrPath - Nome npm (es. \"kiro-memory-plugin-slack\") o percorso assoluto\n * @param displayName - Nome da mostrare nei log (opzionale, default = nameOrPath)\n */\n async loadPlugin(nameOrPath: string, displayName?: string): Promise<void> {\n const label = displayName ?? nameOrPath;\n\n // Ottieni il modulo grezzo (bypass loadFromPath per non richiedere il filesystem)\n const rawModule = await this._loadRawPluginByName(nameOrPath);\n\n const plugin = this.extractPlugin(rawModule);\n this.validatePlugin(plugin as IPlugin);\n const typedPlugin = plugin as IPlugin;\n\n // Memorizza il percorso per il hot reload usando il nome effettivo del plugin\n this.loadedModulePaths.set(typedPlugin.name, nameOrPath);\n // Registra nel registry (stato: registered). init() \u00E8 responsabilit\u00E0 di Registry.enable()\n this.registry.register(typedPlugin);\n\n logger.info('SYSTEM', `PluginLoader: plugin \"${label}\" (v${typedPlugin.version}) caricato`);\n }\n\n /**\n * Ottiene il modulo grezzo per un plugin dato il nome o percorso.\n * Questo metodo pu\u00F2 essere sovrascritta nei test per iniettare moduli mock.\n *\n * La logica \u00E8:\n * 1. Se il nameOrPath \u00E8 un percorso (assoluto o relativo), usa loadFromPath\n * 2. Se \u00E8 un nome, risolve il percorso e usa _loadModule\n */\n protected async _loadRawPluginByName(nameOrPath: string): Promise<unknown> {\n // Per percorsi assoluti o relativi, usa loadFromPath che verifica il filesystem\n if (nameOrPath.startsWith('/') || nameOrPath.startsWith('./') || nameOrPath.startsWith('../')) {\n const plugin = await this.loadFromPath(nameOrPath);\n return { default: plugin };\n }\n\n // Per nomi npm, risolvi e carica\n const modulePath = this.resolveModulePath(nameOrPath);\n return this._loadModule(modulePath);\n }\n\n /**\n * Risolve il percorso assoluto di un plugin dato il nome o il percorso.\n *\n * - Se \u00E8 un percorso assoluto: restituisce com'\u00E8\n * - Se \u00E8 un percorso relativo: risolve rispetto a cwd\n * - Se \u00E8 un nome npm: cerca in node_modules\n * - Se \u00E8 un nome locale: cerca in ~/.contextkit/plugins/<nome>\n */\n private resolveModulePath(nameOrPath: string): string {\n // Percorso assoluto\n if (nameOrPath.startsWith('/')) {\n return nameOrPath;\n }\n\n // Percorso relativo\n if (nameOrPath.startsWith('./') || nameOrPath.startsWith('../')) {\n return resolve(this.projectRoot, nameOrPath);\n }\n\n // Nome npm: cerca in node_modules del progetto\n const nodeModulesPath = join(this.projectRoot, 'node_modules', nameOrPath);\n if (existsSync(nodeModulesPath)) {\n return nodeModulesPath;\n }\n\n // Plugin locale: cerca in ~/.contextkit/plugins/<nome>\n const localPath = join(this.localPluginsDir, nameOrPath);\n if (existsSync(localPath)) {\n return localPath;\n }\n\n // Fallback: restituisce il percorso in node_modules (fallir\u00E0 con errore chiaro)\n return nodeModulesPath;\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Hot reload\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Ricarica un plugin gi\u00E0 caricato: lo distrugge, lo rimuove dal registry\n * e lo reinizializza dal percorso originale.\n *\n * @throws Error se il plugin non \u00E8 stato caricato precedentemente\n */\n async reloadPlugin(name: string): Promise<void> {\n const modulePath = this.loadedModulePaths.get(name);\n if (!modulePath) {\n throw new Error(`Plugin \"${name}\" non trovato \u2014 non \u00E8 stato caricato da questo PluginLoader`);\n }\n\n // Distruggi il plugin esistente se ancora nel registry\n const existing = this.registry.get(name);\n if (existing) {\n try {\n await existing.destroy();\n } catch (err) {\n logger.warn('SYSTEM', `PluginLoader: errore durante destroy() di \"${name}\"`, {}, err as Error);\n }\n this.registry.unregister(name);\n }\n\n // Rimuovi il percorso dalla mappa per consentire la ri-registrazione\n this.loadedModulePaths.delete(name);\n\n // Ricarica dal percorso memorizzato\n logger.info('SYSTEM', `PluginLoader: hot reload di \"${name}\" da ${modulePath}`);\n await this.loadPlugin(modulePath, name);\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // loadAll: discovery + config completa\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Carica tutti i plugin disponibili:\n * 1. Discovery automatico da node_modules (kiro-memory-plugin-*)\n * 2. Plugin locali in ~/.contextkit/plugins/\n * 3. Plugin configurati in config.json\n *\n * I plugin gi\u00E0 registrati non vengono caricati una seconda volta.\n * Ogni errore \u00E8 isolato: un plugin fallito non blocca gli altri.\n *\n * @returns Oggetto con i nomi dei plugin caricati e quelli falliti con errore.\n */\n async loadAll(): Promise<LoadAllResult> {\n const loaded: string[] = [];\n const failed: Array<{ name: string; error: string }> = [];\n\n // \u2500\u2500 1. Discovery da node_modules \u2500\u2500\n const discoveredPaths = await this.discoverPlugins();\n for (const pkgPath of discoveredPaths) {\n // Ricava il nome dal package.json (pi\u00F9 affidabile di basename)\n const pluginName = this.readPackageName(pkgPath) ?? this.basename(pkgPath);\n\n if (this.registry.get(pluginName)) {\n logger.debug('SYSTEM', `PluginLoader: \"${pluginName}\" gi\u00E0 registrato, skip`);\n continue;\n }\n\n try {\n await this.loadPlugin(pkgPath, pluginName);\n loaded.push(pluginName);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.warn('SYSTEM', `PluginLoader: plugin \"${pluginName}\" fallito (node_modules): ${msg}`);\n failed.push({ name: pluginName, error: msg });\n }\n }\n\n // \u2500\u2500 2. Discovery locale da ~/.contextkit/plugins/ \u2500\u2500\n const localPlugins = this.discoverLocalPlugins();\n for (const { name, path: localPath } of localPlugins) {\n if (this.registry.get(name)) {\n logger.debug('SYSTEM', `PluginLoader: \"${name}\" gi\u00E0 registrato, skip`);\n continue;\n }\n\n try {\n await this.loadPlugin(localPath, name);\n loaded.push(name);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.warn('SYSTEM', `PluginLoader: plugin locale \"${name}\" fallito: ${msg}`);\n failed.push({ name, error: msg });\n }\n }\n\n // \u2500\u2500 3. Plugin da configurazione \u2500\u2500\n const config = readConfig();\n const rawPlugins = config['plugins'];\n\n if (rawPlugins) {\n let entries: PluginConfigEntry[] = [];\n try {\n entries = (typeof rawPlugins === 'string'\n ? JSON.parse(rawPlugins)\n : rawPlugins) as PluginConfigEntry[];\n } catch { /* ignora parsing invalido */ }\n\n for (const entry of Array.isArray(entries) ? entries : []) {\n if (entry.enabled === false) continue;\n\n const effectiveName = entry.name;\n if (this.registry.get(effectiveName)) {\n logger.debug('SYSTEM', `PluginLoader: \"${effectiveName}\" gi\u00E0 registrato da config, skip`);\n continue;\n }\n\n try {\n const nameOrPath = entry.path ?? entry.name;\n await this.loadPlugin(nameOrPath, effectiveName);\n loaded.push(effectiveName);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.warn('SYSTEM', `PluginLoader: plugin da config \"${effectiveName}\" fallito: ${msg}`);\n failed.push({ name: effectiveName, error: msg });\n }\n }\n }\n\n logger.info('SYSTEM', `PluginLoader: loadAll completato \u2014 caricati=${loaded.length} falliti=${failed.length}`);\n return { loaded, failed };\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Helpers privati\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Legge il campo `name` dal package.json di un pacchetto.\n * Ritorna null se il file non esiste o il campo non \u00E8 una stringa.\n */\n private readPackageName(pkgDir: string): string | null {\n const pkgJsonPath = join(pkgDir, 'package.json');\n try {\n const raw = readFileSync(pkgJsonPath, 'utf-8');\n const pkg = JSON.parse(raw) as Record<string, unknown>;\n return typeof pkg.name === 'string' ? pkg.name : null;\n } catch {\n return null;\n }\n }\n\n /**\n * Scansiona la directory locale dei plugin (~/.contextkit/plugins/).\n * Ogni sub-directory con un file index.js \u00E8 considerata un plugin.\n */\n private discoverLocalPlugins(): Array<{ name: string; path: string }> {\n if (!existsSync(this.localPluginsDir)) {\n return [];\n }\n\n const result: Array<{ name: string; path: string }> = [];\n\n try {\n const entries = readdirSync(this.localPluginsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const pluginDir = join(this.localPluginsDir, entry.name);\n const indexPath = join(pluginDir, 'index.js');\n\n if (existsSync(indexPath)) {\n result.push({ name: entry.name, path: pluginDir });\n }\n }\n } catch (err) {\n logger.warn('SYSTEM', `PluginLoader: errore durante la scansione di ${this.localPluginsDir}`, {}, err as Error);\n }\n\n return result;\n }\n\n /** Estrae il basename di un percorso (ultimo segmento) */\n private basename(p: string): string {\n return p.split('/').filter(Boolean).pop() ?? p;\n }\n\n /**\n * Costruisce il contesto da passare al plugin durante l'inizializzazione.\n */\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Validazione\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Valida che l'oggetto implementi correttamente IPlugin.\n *\n * Controlli:\n * - Ha i campi obbligatori: name (string), version (string), init (function), destroy (function)\n * - Se presente, minKiroVersion \u00E8 compatibile con la versione corrente\n *\n * @throws Error con messaggio descrittivo se la validazione fallisce\n */\n protected validatePlugin(candidate: unknown): asserts candidate is IPlugin {\n if (!candidate || typeof candidate !== 'object') {\n throw new Error('Il plugin deve essere un oggetto non-null');\n }\n\n const p = candidate as Record<string, unknown>;\n\n if (!p.name || typeof p.name !== 'string') {\n throw new Error('Il plugin deve avere un campo \"name\" di tipo stringa');\n }\n\n if (!p.version || typeof p.version !== 'string') {\n throw new Error(`Plugin \"${p.name}\": campo \"version\" mancante o non stringa`);\n }\n\n if (typeof p.init !== 'function') {\n throw new Error(`Plugin \"${p.name}\": campo \"init\" deve essere una funzione`);\n }\n\n if (typeof p.destroy !== 'function') {\n throw new Error(`Plugin \"${p.name}\": campo \"destroy\" deve essere una funzione`);\n }\n\n // Verifica compatibilit\u00E0 semver semplice (major.minor.patch)\n if (p.minKiroVersion && typeof p.minKiroVersion === 'string') {\n if (!this.isVersionCompatible(KIRO_MEMORY_VERSION, p.minKiroVersion)) {\n throw new Error(\n `Plugin \"${p.name}\": richiede kiro-memory >= ${p.minKiroVersion}, versione corrente: ${KIRO_MEMORY_VERSION}`\n );\n }\n }\n }\n\n /**\n * Confronto semver semplice: verifica che `current` >= `required`.\n * Supporta il formato major.minor.patch; ignora pre-release e metadata.\n */\n private isVersionCompatible(current: string, required: string): boolean {\n const toNumbers = (v: string): number[] =>\n v.split('.').map(part => parseInt(part.replace(/[^0-9]/g, ''), 10) || 0);\n\n const [cMaj, cMin, cPat] = toNumbers(current);\n const [rMaj, rMin, rPat] = toNumbers(required);\n\n if (cMaj !== rMaj) return cMaj > rMaj;\n if (cMin !== rMin) return cMin > rMin;\n return cPat >= rPat;\n }\n}\n", "/**\n * PluginRegistry \u2014 registro singleton dei plugin di Kiro Memory.\n *\n * Responsabilit\u00E0:\n * - Registrare / deregistrare plugin\n * - Gestire il ciclo di vita: registered \u2192 initializing \u2192 active \u2192 destroyed\n * - Emettere hook verso tutti i plugin attivi con isolamento degli errori\n * - Applicare timeout su init(), destroy() e sui singoli hook\n *\n * Isolamento errori: un plugin che lancia eccezione non blocca gli altri.\n * I timeout sono implementati con Promise.race + AbortController-free pattern.\n */\n\nimport { logger } from '../../utils/logger.js';\nimport type { IPlugin, PluginContext, PluginHooks, PluginInfo, PluginLogger, PluginState } from './types.js';\n\n// \u2500\u2500 Costanti di timeout \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst TIMEOUT_INIT_MS = 5_000; // 5 secondi per init()\nconst TIMEOUT_DESTROY_MS = 5_000; // 5 secondi per destroy()\nconst TIMEOUT_HOOK_MS = 10_000; // 10 secondi per ogni hook\n\n// \u2500\u2500 Stato interno per plugin registrato \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface PluginEntry {\n plugin: IPlugin;\n state: PluginState;\n error?: string;\n}\n\n// \u2500\u2500 Helper timeout \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Avvolge una Promise con un timeout.\n * Rigetta con un errore descrittivo se la Promise non si risolve entro timeoutMs.\n */\nfunction withTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new Error(`Timeout dopo ${timeoutMs}ms: ${label}`));\n }, timeoutMs);\n\n promise.then(\n (value) => { clearTimeout(timer); resolve(value); },\n (err) => { clearTimeout(timer); reject(err); }\n );\n });\n}\n\n// \u2500\u2500 PluginRegistry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class PluginRegistry {\n private static _instance: PluginRegistry | null = null;\n\n /** Mappa nome \u2192 entry con stato interno */\n private readonly entries = new Map<string, PluginEntry>();\n\n private constructor() {}\n\n /**\n * Singleton: restituisce l'unica istanza del registry.\n * Il registry \u00E8 condiviso da tutti i moduli del worker.\n */\n static getInstance(): PluginRegistry {\n if (!PluginRegistry._instance) {\n PluginRegistry._instance = new PluginRegistry();\n }\n return PluginRegistry._instance;\n }\n\n /**\n * Resetta l'istanza singleton \u2014 solo per uso nei test.\n * @internal\n */\n static _resetForTests(): void {\n PluginRegistry._instance = null;\n }\n\n // \u2500\u2500 Registrazione \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Registra un plugin. Dopo la registrazione il plugin \u00E8 in stato 'registered'\n * e non \u00E8 ancora attivo: chiama enable() per inizializzarlo.\n *\n * Lancia un errore se il nome \u00E8 gi\u00E0 in uso.\n */\n register(plugin: IPlugin): void {\n if (this.entries.has(plugin.name)) {\n throw new Error(`Plugin gi\u00E0 registrato: \"${plugin.name}\"`);\n }\n\n this.entries.set(plugin.name, {\n plugin,\n state: 'registered',\n });\n\n logger.info('SYSTEM', `[PluginRegistry] Plugin registrato: ${plugin.name}@${plugin.version}`);\n }\n\n /**\n * Deregistra un plugin: chiama destroy() se attivo, poi rimuove dal registry.\n * Non lancia errori se il plugin non esiste.\n */\n async unregister(name: string): Promise<void> {\n const entry = this.entries.get(name);\n if (!entry) return;\n\n if (entry.state === 'active') {\n await this._runDestroy(entry);\n }\n\n this.entries.delete(name);\n logger.info('SYSTEM', `[PluginRegistry] Plugin rimosso: ${name}`);\n }\n\n // \u2500\u2500 Accesso \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Restituisce lo snapshot di tutti i plugin registrati con il loro stato.\n */\n getAll(): PluginInfo[] {\n return Array.from(this.entries.values()).map((e) => ({\n name: e.plugin.name,\n version: e.plugin.version,\n description: e.plugin.description,\n state: e.state,\n error: e.error,\n }));\n }\n\n /**\n * Restituisce il plugin per nome, o undefined se non registrato.\n */\n get(name: string): IPlugin | undefined {\n return this.entries.get(name)?.plugin;\n }\n\n // \u2500\u2500 Lifecycle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Abilita un plugin: chiama init(context) con timeout 5s.\n * Imposta lo stato su 'active' in caso di successo, 'error' altrimenti.\n *\n * Non lancia eccezioni verso il chiamante: l'errore \u00E8 catturato e loggato.\n */\n async enable(name: string, context: PluginContext): Promise<void> {\n const entry = this.entries.get(name);\n if (!entry) {\n throw new Error(`Plugin non trovato: \"${name}\"`);\n }\n\n if (entry.state === 'active') {\n logger.info('SYSTEM', `[PluginRegistry] Plugin gi\u00E0 attivo: ${name}`);\n return;\n }\n\n entry.state = 'initializing';\n entry.error = undefined;\n\n try {\n await withTimeout(\n entry.plugin.init(context),\n TIMEOUT_INIT_MS,\n `${name}.init()`\n );\n\n entry.state = 'active';\n logger.info('SYSTEM', `[PluginRegistry] Plugin attivato: ${name}`);\n } catch (err) {\n entry.state = 'error';\n entry.error = err instanceof Error ? err.message : String(err);\n logger.error('SYSTEM', `[PluginRegistry] init() fallito per \"${name}\": ${entry.error}`);\n // Non rilanciamo per non bloccare il worker al bootstrap\n }\n }\n\n /**\n * Disabilita un plugin: chiama destroy() con timeout 5s.\n * Imposta lo stato su 'destroyed' in caso di successo, 'error' altrimenti.\n */\n async disable(name: string): Promise<void> {\n const entry = this.entries.get(name);\n if (!entry) {\n throw new Error(`Plugin non trovato: \"${name}\"`);\n }\n\n if (entry.state !== 'active') {\n logger.info('SYSTEM', `[PluginRegistry] Plugin non attivo, skip destroy: ${name}`);\n return;\n }\n\n await this._runDestroy(entry);\n }\n\n // \u2500\u2500 Emissione hook \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Emette un hook verso tutti i plugin attivi che lo implementano.\n *\n * Ogni plugin \u00E8 chiamato in parallelo con isolamento degli errori:\n * se un plugin fallisce o va in timeout, gli altri continuano.\n *\n * @param hookName - Chiave dell'hook in PluginHooks (es. 'onObservation')\n * @param payload - Payload tipizzato per quell'hook\n */\n async emitHook<K extends keyof PluginHooks>(\n hookName: K,\n payload: Parameters<NonNullable<PluginHooks[K]>>[0]\n ): Promise<void> {\n const activeEntries = Array.from(this.entries.values()).filter(\n (e) => e.state === 'active' && e.plugin.hooks?.[hookName]\n );\n\n if (activeEntries.length === 0) return;\n\n // Esecuzione parallela con isolamento errori e timeout per ogni plugin\n await Promise.allSettled(\n activeEntries.map((entry) => {\n const hookFn = entry.plugin.hooks![hookName] as\n | ((p: typeof payload) => Promise<void>)\n | undefined;\n\n if (!hookFn) return Promise.resolve();\n\n const hookPromise = hookFn(payload);\n\n return withTimeout(\n hookPromise,\n TIMEOUT_HOOK_MS,\n `${entry.plugin.name}.hooks.${hookName}()`\n ).catch((err: unknown) => {\n // Isolamento: logga ma non propaga l'errore\n const msg = err instanceof Error ? err.message : String(err);\n logger.warn(\n 'SYSTEM',\n `[PluginRegistry] Hook ${hookName} fallito in \"${entry.plugin.name}\": ${msg}`\n );\n });\n })\n );\n }\n\n // \u2500\u2500 Metodi privati \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Esegue destroy() con timeout e aggiorna lo stato dell'entry. */\n private async _runDestroy(entry: PluginEntry): Promise<void> {\n try {\n await withTimeout(\n entry.plugin.destroy(),\n TIMEOUT_DESTROY_MS,\n `${entry.plugin.name}.destroy()`\n );\n\n entry.state = 'destroyed';\n logger.info('SYSTEM', `[PluginRegistry] Plugin disabilitato: ${entry.plugin.name}`);\n } catch (err) {\n entry.state = 'error';\n entry.error = err instanceof Error ? err.message : String(err);\n logger.error(\n 'SYSTEM',\n `[PluginRegistry] destroy() fallito per \"${entry.plugin.name}\": ${entry.error}`\n );\n }\n }\n}\n\n// \u2500\u2500 Factory del logger per plugin \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Crea un PluginLogger che scrive sul logger di sistema con prefisso [PLUGIN:<name>].\n */\nexport function createPluginLogger(pluginName: string): PluginLogger {\n const prefix = `[PLUGIN:${pluginName}]`;\n return {\n info: (msg, ...args) => logger.info('SYSTEM', `${prefix} ${msg}`, {}, ...args),\n warn: (msg, ...args) => logger.warn('SYSTEM', `${prefix} ${msg}`, {}, ...args),\n error: (msg, ...args) => logger.error('SYSTEM', `${prefix} ${msg}`, {}, ...args),\n };\n}\n\n// \u2500\u2500 Esportazione del tipo PluginLogger \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type { PluginLogger } from './types.js';\n", "/**\n * Router Core: health check, SSE events, notify endpoint.\n * Manages the base worker infrastructure.\n */\n\nimport { Router } from 'express';\nimport rateLimit from 'express-rate-limit';\nimport type { WorkerContext } from '../worker-context.js';\nimport { getClients, getMaxSSEClients, addClient, removeClient } from '../worker-context.js';\nimport { logger } from '../../utils/logger.js';\nimport { VERSION } from '../../index.js';\n\nconst ALLOWED_EVENTS = new Set([\n 'observation-created',\n 'summary-created',\n 'prompt-created',\n 'session-created'\n]);\n\nexport function createCoreRouter(ctx: WorkerContext, workerToken: string): Router {\n const router = Router();\n\n // Dedicated rate limit for /api/notify (more restrictive)\n const notifyLimiter = rateLimit({\n windowMs: 60_000,\n max: 60,\n standardHeaders: true,\n legacyHeaders: false\n });\n\n // Notification from hooks \u2192 SSE broadcast to dashboard clients\n router.post('/api/notify', notifyLimiter, (req, res) => {\n const token = req.headers['x-worker-token'] as string;\n if (token !== workerToken) {\n res.status(401).json({ error: 'Invalid or missing X-Worker-Token' });\n return;\n }\n\n const { event, data } = req.body || {};\n if (!event || typeof event !== 'string' || !ALLOWED_EVENTS.has(event)) {\n res.status(400).json({ error: `Event must be one of: ${[...ALLOWED_EVENTS].join(', ')}` });\n return;\n }\n\n ctx.broadcast(event, data || {});\n res.json({ ok: true });\n });\n\n // Health check\n router.get('/health', (_req, res) => {\n res.json({\n status: 'ok',\n timestamp: Date.now(),\n version: VERSION\n });\n });\n\n // SSE endpoint with keepalive and connection limit\n router.get('/events', (req, res) => {\n const clients = getClients();\n if (clients.length >= getMaxSSEClients()) {\n res.status(503).json({ error: 'Too many SSE connections' });\n return;\n }\n\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no');\n res.flushHeaders();\n\n addClient(res);\n logger.info('WORKER', 'SSE client connected', { clients: clients.length });\n\n // Initial connection event\n res.write(`event: connected\\ndata: ${JSON.stringify({ timestamp: Date.now() })}\\n\\n`);\n\n // Keepalive every 15 seconds\n const keepaliveInterval = setInterval(() => {\n try {\n res.write(`:keepalive ${Date.now()}\\n\\n`);\n } catch {\n clearInterval(keepaliveInterval);\n }\n }, 15000);\n\n req.on('close', () => {\n clearInterval(keepaliveInterval);\n removeClient(res);\n logger.info('WORKER', 'SSE client disconnected', { clients: getClients().length });\n });\n });\n\n return router;\n}\n", "/**\n * Keyset pagination cursor per Kiro Memory.\n *\n * Il cursor codifica in base64 la coppia (created_at_epoch, id) che identifica\n * univocamente l'ultima riga restituita dalla pagina precedente.\n * Il formato interno \u00E8: `<epoch>:<id>` \u2014 entrambi interi positivi.\n *\n * Utilizzo SQL:\n * WHERE (created_at_epoch, id) < (?, ?)\n * ORDER BY created_at_epoch DESC, id DESC\n * LIMIT ?\n */\n\n/** Struttura decodificata di un cursor */\nexport interface DecodedCursor {\n epoch: number;\n id: number;\n}\n\n/**\n * Codifica un cursor a partire da epoch e id dell'ultimo elemento della pagina.\n * Restituisce una stringa base64 URL-safe.\n */\nexport function encodeCursor(id: number, epoch: number): string {\n const raw = `${epoch}:${id}`;\n return Buffer.from(raw, 'utf8').toString('base64url');\n}\n\n/**\n * Decodifica un cursor base64 nella coppia {epoch, id}.\n * Restituisce null se il formato \u00E8 invalido o i valori non sono interi positivi.\n */\nexport function decodeCursor(cursor: string): DecodedCursor | null {\n try {\n const raw = Buffer.from(cursor, 'base64url').toString('utf8');\n const colonIdx = raw.indexOf(':');\n if (colonIdx === -1) return null;\n\n const epochStr = raw.substring(0, colonIdx);\n const idStr = raw.substring(colonIdx + 1);\n\n const epoch = parseInt(epochStr, 10);\n const id = parseInt(idStr, 10);\n\n // Entrambi devono essere interi positivi validi\n if (!Number.isInteger(epoch) || epoch <= 0) return null;\n if (!Number.isInteger(id) || id <= 0) return null;\n\n return { epoch, id };\n } catch {\n return null;\n }\n}\n\n/**\n * Parametri di input per una query paginata con keyset.\n */\nexport interface KeysetPageParams {\n /** Cursor opaco (base64) dall'ultima risposta. Null = prima pagina. */\n cursor: string | null;\n /** Numero di elementi per pagina (default 50, max 200). */\n limit: number;\n /** Filtro opzionale per progetto. */\n project?: string;\n}\n\n/**\n * Risposta standardizzata per endpoint con keyset pagination.\n */\nexport interface KeysetPageResult<T> {\n /** Elementi della pagina corrente */\n data: T[];\n /** Cursor da passare alla prossima richiesta. Null se non ci sono pi\u00F9 pagine. */\n next_cursor: string | null;\n /** true se esiste almeno un'altra pagina oltre questa */\n has_more: boolean;\n}\n\n/**\n * Costruisce il cursor per il prossimo fetch a partire dall'ultimo elemento della pagina.\n * Restituisce null se la lista \u00E8 vuota o manca di campi necessari.\n */\nexport function buildNextCursor<T extends { id: number; created_at_epoch: number }>(\n rows: T[],\n limit: number\n): string | null {\n // Se la pagina \u00E8 piena (length === limit), ci sono probabilmente altre righe\n if (rows.length < limit) return null;\n\n const last = rows[rows.length - 1];\n if (!last) return null;\n\n return encodeCursor(last.id, last.created_at_epoch);\n}\n", "import { Database } from 'bun:sqlite';\nimport type { DBSession } from '../../types/worker-types.js';\n\n/**\n * Session operations for Kiro Memory database\n */\n\nexport function createSession(\n db: Database,\n contentSessionId: string,\n project: string,\n userPrompt: string\n): number {\n const now = new Date();\n const result = db.run(\n `INSERT INTO sessions (content_session_id, project, user_prompt, status, started_at, started_at_epoch)\n VALUES (?, ?, ?, 'active', ?, ?)`,\n [contentSessionId, project, userPrompt, now.toISOString(), now.getTime()]\n );\n return Number(result.lastInsertRowid);\n}\n\nexport function getSessionByContentId(db: Database, contentSessionId: string): DBSession | null {\n const query = db.query('SELECT * FROM sessions WHERE content_session_id = ?');\n return query.get(contentSessionId) as DBSession | null;\n}\n\nexport function getSessionById(db: Database, id: number): DBSession | null {\n const query = db.query('SELECT * FROM sessions WHERE id = ?');\n return query.get(id) as DBSession | null;\n}\n\nexport function updateSessionMemoryId(\n db: Database,\n id: number,\n memorySessionId: string\n): void {\n db.run(\n 'UPDATE sessions SET memory_session_id = ? WHERE id = ?',\n [memorySessionId, id]\n );\n}\n\nexport function completeSession(db: Database, id: number): void {\n const now = new Date();\n db.run(\n `UPDATE sessions \n SET status = 'completed', completed_at = ?, completed_at_epoch = ?\n WHERE id = ?`,\n [now.toISOString(), now.getTime(), id]\n );\n}\n\nexport function failSession(db: Database, id: number): void {\n const now = new Date();\n db.run(\n `UPDATE sessions \n SET status = 'failed', completed_at = ?, completed_at_epoch = ?\n WHERE id = ?`,\n [now.toISOString(), now.getTime(), id]\n );\n}\n\nexport function getActiveSessions(db: Database): DBSession[] {\n const query = db.query('SELECT * FROM sessions WHERE status = \\'active\\' ORDER BY started_at_epoch DESC');\n return query.all() as DBSession[];\n}\n\nexport function getAllSessions(db: Database, limit: number = 100): DBSession[] {\n const query = db.query('SELECT * FROM sessions ORDER BY started_at_epoch DESC LIMIT ?');\n return query.all(limit) as DBSession[];\n}\n\nexport function getSessionsByProject(db: Database, project: string, limit: number = 100): DBSession[] {\n const query = db.query('SELECT * FROM sessions WHERE project = ? ORDER BY started_at_epoch DESC LIMIT ?');\n return query.all(project, limit) as DBSession[];\n}\n", "// Export database\nexport { KiroMemoryDatabase } from './Database.js';\n\n// Export keyset pagination utilities\nexport { encodeCursor, decodeCursor, buildNextCursor } from './cursor.js';\nexport type { DecodedCursor, KeysetPageParams, KeysetPageResult } from './cursor.js';\n\n// Export CRUD operations\nexport * from './Sessions.js';\nexport * from './Observations.js';\nexport * from './Summaries.js';\nexport * from './Prompts.js';\n\n// Export checkpoints\nexport * from './Checkpoints.js';\n\n// Export reports\nexport * from './Reports.js';\n\n// Export advanced search\nexport * from './Search.js';\n\n// Export GitHub links\nexport * from './GithubLinks.js';\n\n// Export import/export JSONL\nexport * from './ImportExport.js';\n\n// Export politiche di retention\nexport * from './Retention.js';\n\n// Export backup\nexport * from './Backup.js';\n", "import { Database } from 'bun:sqlite';\nimport type { Summary } from '../../types/worker-types.js';\n\n/**\n * Summary operations for Kiro Memory database\n */\n\n/** Escape dei caratteri wildcard LIKE per prevenire pattern injection */\nfunction escapeLikePattern(input: string): string {\n return input.replace(/[%_\\\\]/g, '\\\\$&');\n}\n\nexport function createSummary(\n db: Database,\n sessionId: string,\n project: string,\n request: string | null,\n investigated: string | null,\n learned: string | null,\n completed: string | null,\n nextSteps: string | null,\n notes: string | null\n): number {\n const now = new Date();\n const result = db.run(\n `INSERT INTO summaries \n (session_id, project, request, investigated, learned, completed, next_steps, notes, created_at, created_at_epoch)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [sessionId, project, request, investigated, learned, completed, nextSteps, notes, now.toISOString(), now.getTime()]\n );\n return Number(result.lastInsertRowid);\n}\n\nexport function getSummaryBySession(db: Database, sessionId: string): Summary | null {\n const query = db.query('SELECT * FROM summaries WHERE session_id = ? ORDER BY created_at_epoch DESC, id DESC LIMIT 1');\n return query.get(sessionId) as Summary | null;\n}\n\nexport function getSummariesByProject(db: Database, project: string, limit: number = 50): Summary[] {\n const query = db.query(\n 'SELECT * FROM summaries WHERE project = ? ORDER BY created_at_epoch DESC, id DESC LIMIT ?'\n );\n return query.all(project, limit) as Summary[];\n}\n\nexport function searchSummaries(db: Database, searchTerm: string, project?: string): Summary[] {\n const sql = project\n ? `SELECT * FROM summaries\n WHERE project = ? AND (request LIKE ? ESCAPE '\\\\' OR learned LIKE ? ESCAPE '\\\\' OR completed LIKE ? ESCAPE '\\\\' OR notes LIKE ? ESCAPE '\\\\')\n ORDER BY created_at_epoch DESC, id DESC`\n : `SELECT * FROM summaries\n WHERE request LIKE ? ESCAPE '\\\\' OR learned LIKE ? ESCAPE '\\\\' OR completed LIKE ? ESCAPE '\\\\' OR notes LIKE ? ESCAPE '\\\\'\n ORDER BY created_at_epoch DESC, id DESC`;\n\n const pattern = `%${escapeLikePattern(searchTerm)}%`;\n const query = db.query(sql);\n\n if (project) {\n return query.all(project, pattern, pattern, pattern, pattern) as Summary[];\n }\n return query.all(pattern, pattern, pattern, pattern) as Summary[];\n}\n\nexport function deleteSummary(db: Database, id: number): void {\n db.run('DELETE FROM summaries WHERE id = ?', [id]);\n}\n", "import { Database } from 'bun:sqlite';\nimport type { UserPrompt } from '../../types/worker-types.js';\n\n/**\n * Prompt operations for Kiro Memory database\n */\n\nexport function createPrompt(\n db: Database,\n contentSessionId: string,\n project: string,\n promptNumber: number,\n promptText: string\n): number {\n const now = new Date();\n const result = db.run(\n `INSERT INTO prompts \n (content_session_id, project, prompt_number, prompt_text, created_at, created_at_epoch)\n VALUES (?, ?, ?, ?, ?, ?)`,\n [contentSessionId, project, promptNumber, promptText, now.toISOString(), now.getTime()]\n );\n return Number(result.lastInsertRowid);\n}\n\nexport function getPromptsBySession(db: Database, contentSessionId: string): UserPrompt[] {\n const query = db.query(\n 'SELECT * FROM prompts WHERE content_session_id = ? ORDER BY prompt_number ASC'\n );\n return query.all(contentSessionId) as UserPrompt[];\n}\n\nexport function getPromptsByProject(db: Database, project: string, limit: number = 100): UserPrompt[] {\n const query = db.query(\n 'SELECT * FROM prompts WHERE project = ? ORDER BY created_at_epoch DESC, id DESC LIMIT ?'\n );\n return query.all(project, limit) as UserPrompt[];\n}\n\nexport function getLatestPrompt(db: Database, contentSessionId: string): UserPrompt | null {\n const query = db.query(\n 'SELECT * FROM prompts WHERE content_session_id = ? ORDER BY prompt_number DESC LIMIT 1'\n );\n return query.get(contentSessionId) as UserPrompt | null;\n}\n\nexport function deletePrompt(db: Database, id: number): void {\n db.run('DELETE FROM prompts WHERE id = ?', [id]);\n}\n", "import { Database } from 'bun:sqlite';\nimport type { DBCheckpoint } from '../../types/worker-types.js';\n\n/**\n * Checkpoint operations per Kiro Memory database.\n * I checkpoint salvano uno snapshot strutturato della sessione per resume futuro.\n */\n\nexport function createCheckpoint(\n db: Database,\n sessionId: number,\n project: string,\n data: {\n task: string;\n progress?: string;\n nextSteps?: string;\n openQuestions?: string;\n relevantFiles?: string;\n contextSnapshot?: string;\n }\n): number {\n const now = new Date();\n const result = db.run(\n `INSERT INTO checkpoints (session_id, project, task, progress, next_steps, open_questions, relevant_files, context_snapshot, created_at, created_at_epoch)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n sessionId,\n project,\n data.task,\n data.progress || null,\n data.nextSteps || null,\n data.openQuestions || null,\n data.relevantFiles || null,\n data.contextSnapshot || null,\n now.toISOString(),\n now.getTime()\n ]\n );\n return Number(result.lastInsertRowid);\n}\n\nexport function getLatestCheckpoint(db: Database, sessionId: number): DBCheckpoint | null {\n const query = db.query(\n 'SELECT * FROM checkpoints WHERE session_id = ? ORDER BY created_at_epoch DESC, id DESC LIMIT 1'\n );\n return query.get(sessionId) as DBCheckpoint | null;\n}\n\nexport function getLatestCheckpointByProject(db: Database, project: string): DBCheckpoint | null {\n const query = db.query(\n 'SELECT * FROM checkpoints WHERE project = ? ORDER BY created_at_epoch DESC, id DESC LIMIT 1'\n );\n return query.get(project) as DBCheckpoint | null;\n}\n\nexport function getCheckpointsBySession(db: Database, sessionId: number): DBCheckpoint[] {\n const query = db.query(\n 'SELECT * FROM checkpoints WHERE session_id = ? ORDER BY created_at_epoch DESC, id DESC'\n );\n return query.all(sessionId) as DBCheckpoint[];\n}\n", "import { Database } from 'bun:sqlite';\nimport type { ReportData } from '../../types/worker-types.js';\n\n/**\n * Report module for Kiro Memory.\n * Aggregates metrics for a specific time range.\n */\n\n/**\n * Generate aggregated data for an activity report.\n * Executes 8 queries on the range [startEpoch, endEpoch].\n */\nexport function getReportData(\n db: Database,\n project: string | undefined,\n startEpoch: number,\n endEpoch: number\n): ReportData {\n // Calculate period\n const startDate = new Date(startEpoch);\n const endDate = new Date(endEpoch);\n const days = Math.ceil((endEpoch - startEpoch) / (24 * 60 * 60 * 1000));\n const label = days <= 7 ? 'Weekly' : days <= 31 ? 'Monthly' : 'Custom';\n\n // Helper for queries with project filter + time range\n const countInRange = (table: string, epochCol: string = 'created_at_epoch'): number => {\n const sql = project\n ? `SELECT COUNT(*) as count FROM ${table} WHERE project = ? AND ${epochCol} >= ? AND ${epochCol} <= ?`\n : `SELECT COUNT(*) as count FROM ${table} WHERE ${epochCol} >= ? AND ${epochCol} <= ?`;\n const stmt = db.query(sql);\n const row = project\n ? stmt.get(project, startEpoch, endEpoch) as any\n : stmt.get(startEpoch, endEpoch) as any;\n return row?.count || 0;\n };\n\n // 1. Base counts in the period\n const observations = countInRange('observations');\n const summaries = countInRange('summaries');\n const prompts = countInRange('prompts');\n // Sessions: use started_at_epoch as reference\n const sessions = countInRange('sessions', 'started_at_epoch');\n\n // 2. Daily timeline\n const timelineSql = project\n ? `SELECT DATE(datetime(created_at_epoch / 1000, 'unixepoch')) as day, COUNT(*) as count\n FROM observations\n WHERE project = ? AND created_at_epoch >= ? AND created_at_epoch <= ?\n GROUP BY day ORDER BY day ASC`\n : `SELECT DATE(datetime(created_at_epoch / 1000, 'unixepoch')) as day, COUNT(*) as count\n FROM observations\n WHERE created_at_epoch >= ? AND created_at_epoch <= ?\n GROUP BY day ORDER BY day ASC`;\n const timelineStmt = db.query(timelineSql);\n const timeline = (project\n ? timelineStmt.all(project, startEpoch, endEpoch)\n : timelineStmt.all(startEpoch, endEpoch)\n ) as Array<{ day: string; count: number }>;\n\n // 3. Distribution by type\n const typeSql = project\n ? `SELECT type, COUNT(*) as count FROM observations\n WHERE project = ? AND created_at_epoch >= ? AND created_at_epoch <= ?\n GROUP BY type ORDER BY count DESC`\n : `SELECT type, COUNT(*) as count FROM observations\n WHERE created_at_epoch >= ? AND created_at_epoch <= ?\n GROUP BY type ORDER BY count DESC`;\n const typeStmt = db.query(typeSql);\n const typeDistribution = (project\n ? typeStmt.all(project, startEpoch, endEpoch)\n : typeStmt.all(startEpoch, endEpoch)\n ) as Array<{ type: string; count: number }>;\n\n // 4. Session stats in the period\n const sessionTotalSql = project\n ? `SELECT COUNT(*) as count FROM sessions WHERE project = ? AND started_at_epoch >= ? AND started_at_epoch <= ?`\n : `SELECT COUNT(*) as count FROM sessions WHERE started_at_epoch >= ? AND started_at_epoch <= ?`;\n const sessionTotal = (project\n ? (db.query(sessionTotalSql).get(project, startEpoch, endEpoch) as any)?.count\n : (db.query(sessionTotalSql).get(startEpoch, endEpoch) as any)?.count\n ) || 0;\n\n const sessionCompletedSql = project\n ? `SELECT COUNT(*) as count FROM sessions WHERE project = ? AND started_at_epoch >= ? AND started_at_epoch <= ? AND status = 'completed'`\n : `SELECT COUNT(*) as count FROM sessions WHERE started_at_epoch >= ? AND started_at_epoch <= ? AND status = 'completed'`;\n const sessionCompleted = (project\n ? (db.query(sessionCompletedSql).get(project, startEpoch, endEpoch) as any)?.count\n : (db.query(sessionCompletedSql).get(startEpoch, endEpoch) as any)?.count\n ) || 0;\n\n const sessionAvgSql = project\n ? `SELECT AVG((completed_at_epoch - started_at_epoch) / 1000.0 / 60.0) as avg_min\n FROM sessions\n WHERE project = ? AND started_at_epoch >= ? AND started_at_epoch <= ?\n AND status = 'completed' AND completed_at_epoch IS NOT NULL AND completed_at_epoch > started_at_epoch`\n : `SELECT AVG((completed_at_epoch - started_at_epoch) / 1000.0 / 60.0) as avg_min\n FROM sessions\n WHERE started_at_epoch >= ? AND started_at_epoch <= ?\n AND status = 'completed' AND completed_at_epoch IS NOT NULL AND completed_at_epoch > started_at_epoch`;\n const avgRow = project\n ? db.query(sessionAvgSql).get(project, startEpoch, endEpoch) as any\n : db.query(sessionAvgSql).get(startEpoch, endEpoch) as any;\n const avgDurationMinutes = Math.round((avgRow?.avg_min || 0) * 10) / 10;\n\n // 5. Knowledge count\n const knowledgeSql = project\n ? `SELECT COUNT(*) as count FROM observations\n WHERE project = ? AND created_at_epoch >= ? AND created_at_epoch <= ?\n AND type IN ('constraint', 'decision', 'heuristic', 'rejected')`\n : `SELECT COUNT(*) as count FROM observations\n WHERE created_at_epoch >= ? AND created_at_epoch <= ?\n AND type IN ('constraint', 'decision', 'heuristic', 'rejected')`;\n const knowledgeCount = (project\n ? (db.query(knowledgeSql).get(project, startEpoch, endEpoch) as any)?.count\n : (db.query(knowledgeSql).get(startEpoch, endEpoch) as any)?.count\n ) || 0;\n\n // 6. Stale count\n const staleSql = project\n ? `SELECT COUNT(*) as count FROM observations\n WHERE project = ? AND created_at_epoch >= ? AND created_at_epoch <= ? AND is_stale = 1`\n : `SELECT COUNT(*) as count FROM observations\n WHERE created_at_epoch >= ? AND created_at_epoch <= ? AND is_stale = 1`;\n const staleCount = (project\n ? (db.query(staleSql).get(project, startEpoch, endEpoch) as any)?.count\n : (db.query(staleSql).get(startEpoch, endEpoch) as any)?.count\n ) || 0;\n\n // 7. Summary contents (learnings, completed, next steps)\n const summarySql = project\n ? `SELECT learned, completed, next_steps FROM summaries\n WHERE project = ? AND created_at_epoch >= ? AND created_at_epoch <= ?\n ORDER BY created_at_epoch DESC, id DESC`\n : `SELECT learned, completed, next_steps FROM summaries\n WHERE created_at_epoch >= ? AND created_at_epoch <= ?\n ORDER BY created_at_epoch DESC, id DESC`;\n const summaryRows = (project\n ? db.query(summarySql).all(project, startEpoch, endEpoch)\n : db.query(summarySql).all(startEpoch, endEpoch)\n ) as Array<{ learned: string | null; completed: string | null; next_steps: string | null }>;\n\n const topLearnings: string[] = [];\n const completedTasks: string[] = [];\n const nextStepsArr: string[] = [];\n\n for (const row of summaryRows) {\n if (row.learned) {\n // Split by '; ' if concatenated (hook stop.ts format)\n const parts = row.learned.split('; ').filter(Boolean);\n topLearnings.push(...parts);\n }\n if (row.completed) {\n const parts = row.completed.split('; ').filter(Boolean);\n completedTasks.push(...parts);\n }\n if (row.next_steps) {\n const parts = row.next_steps.split('; ').filter(Boolean);\n nextStepsArr.push(...parts);\n }\n }\n\n // 8. File hotspots (most modified files in the period)\n const filesSql = project\n ? `SELECT files_modified FROM observations\n WHERE project = ? AND created_at_epoch >= ? AND created_at_epoch <= ?\n AND files_modified IS NOT NULL AND files_modified != ''`\n : `SELECT files_modified FROM observations\n WHERE created_at_epoch >= ? AND created_at_epoch <= ?\n AND files_modified IS NOT NULL AND files_modified != ''`;\n const fileRows = (project\n ? db.query(filesSql).all(project, startEpoch, endEpoch)\n : db.query(filesSql).all(startEpoch, endEpoch)\n ) as Array<{ files_modified: string }>;\n\n const fileCounts = new Map<string, number>();\n for (const row of fileRows) {\n const files = row.files_modified.split(',').map(f => f.trim()).filter(Boolean);\n for (const file of files) {\n fileCounts.set(file, (fileCounts.get(file) || 0) + 1);\n }\n }\n\n const fileHotspots = Array.from(fileCounts.entries())\n .map(([file, count]) => ({ file, count }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 15);\n\n return {\n period: {\n start: startDate.toISOString().split('T')[0],\n end: endDate.toISOString().split('T')[0],\n days,\n label\n },\n overview: {\n observations,\n summaries,\n sessions,\n prompts,\n knowledgeCount,\n staleCount\n },\n timeline,\n typeDistribution,\n sessionStats: {\n total: sessionTotal,\n completed: sessionCompleted,\n avgDurationMinutes\n },\n topLearnings: [...new Set(topLearnings)].slice(0, 10),\n completedTasks: [...new Set(completedTasks)].slice(0, 10),\n nextSteps: [...new Set(nextStepsArr)].slice(0, 10),\n fileHotspots\n };\n}\n", "/**\n * Operazioni CRUD per la tabella github_links.\n *\n * Ogni funzione riceve la Database instance come primo parametro\n * (stessa convenzione degli altri moduli sqlite del progetto).\n */\n\nimport { Database } from 'bun:sqlite';\n\n// \u2500\u2500 Tipi \u2500\u2500\n\nexport interface GithubLink {\n id: number;\n observation_id: number | null;\n session_id: string | null;\n repo: string;\n issue_number: number | null;\n pr_number: number | null;\n event_type: string;\n action: string | null;\n title: string | null;\n url: string | null;\n author: string | null;\n created_at: string;\n created_at_epoch: number;\n}\n\nexport interface CreateGithubLinkData {\n observation_id?: number | null;\n session_id?: string | null;\n repo: string;\n issue_number?: number | null;\n pr_number?: number | null;\n event_type: string;\n action?: string | null;\n title?: string | null;\n url?: string | null;\n author?: string | null;\n}\n\nexport interface GithubLinksSearchOptions {\n repo?: string;\n event_type?: string;\n limit?: number;\n}\n\nexport interface RepoLinkCount {\n repo: string;\n count: number;\n last_event_at: string;\n}\n\n// \u2500\u2500 Funzioni CRUD \u2500\u2500\n\n/**\n * Inserisce un nuovo link GitHub nel database.\n * Restituisce l'ID della riga appena creata.\n */\nexport function createGithubLink(db: Database, data: CreateGithubLinkData): number {\n const now = new Date();\n const result = db.run(\n `INSERT INTO github_links\n (observation_id, session_id, repo, issue_number, pr_number, event_type,\n action, title, url, author, created_at, created_at_epoch)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n data.observation_id ?? null,\n data.session_id ?? null,\n data.repo,\n data.issue_number ?? null,\n data.pr_number ?? null,\n data.event_type,\n data.action ?? null,\n data.title ?? null,\n data.url ?? null,\n data.author ?? null,\n now.toISOString(),\n now.getTime(),\n ]\n );\n return Number(result.lastInsertRowid);\n}\n\n/**\n * Recupera tutti i link associati a una specifica observation.\n * Ordinati per data di creazione decrescente.\n */\nexport function getGithubLinksByObservation(db: Database, observationId: number): GithubLink[] {\n return db.query(\n `SELECT * FROM github_links\n WHERE observation_id = ?\n ORDER BY created_at_epoch DESC, id DESC`\n ).all(observationId) as GithubLink[];\n}\n\n/**\n * Recupera i link per un dato repository con limite opzionale.\n * Ordinati per data di creazione decrescente.\n */\nexport function getGithubLinksByRepo(\n db: Database,\n repo: string,\n limit: number = 50\n): GithubLink[] {\n return db.query(\n `SELECT * FROM github_links\n WHERE repo = ?\n ORDER BY created_at_epoch DESC, id DESC\n LIMIT ?`\n ).all(repo, limit) as GithubLink[];\n}\n\n/**\n * Recupera i link relativi a una specifica issue di un repository.\n * Ordinati per data di creazione decrescente.\n */\nexport function getGithubLinksByIssue(\n db: Database,\n repo: string,\n issueNumber: number\n): GithubLink[] {\n return db.query(\n `SELECT * FROM github_links\n WHERE repo = ? AND issue_number = ?\n ORDER BY created_at_epoch DESC, id DESC`\n ).all(repo, issueNumber) as GithubLink[];\n}\n\n/**\n * Recupera i link relativi a una specifica Pull Request di un repository.\n * Ordinati per data di creazione decrescente.\n */\nexport function getGithubLinksByPR(\n db: Database,\n repo: string,\n prNumber: number\n): GithubLink[] {\n return db.query(\n `SELECT * FROM github_links\n WHERE repo = ? AND pr_number = ?\n ORDER BY created_at_epoch DESC, id DESC`\n ).all(repo, prNumber) as GithubLink[];\n}\n\n/**\n * Ricerca nei link GitHub per repository, tipo di evento e/o testo nel titolo.\n * Supporta filtro per repo e event_type con un limite configurabile.\n */\nexport function searchGithubLinks(\n db: Database,\n query: string,\n options: GithubLinksSearchOptions = {}\n): GithubLink[] {\n const { repo, event_type, limit = 50 } = options;\n const safeLimit = Math.min(Math.max(1, limit), 200);\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filtro full-text su title o url\n if (query && query.trim().length > 0) {\n const pattern = `%${query.replace(/[%_\\\\]/g, '\\\\$&')}%`;\n conditions.push(`(title LIKE ? ESCAPE '\\\\' OR url LIKE ? ESCAPE '\\\\')`);\n params.push(pattern, pattern);\n }\n\n if (repo) {\n conditions.push('repo = ?');\n params.push(repo);\n }\n\n if (event_type) {\n conditions.push('event_type = ?');\n params.push(event_type);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n params.push(safeLimit);\n\n return db.query(\n `SELECT * FROM github_links\n ${where}\n ORDER BY created_at_epoch DESC, id DESC\n LIMIT ?`\n ).all(...params) as GithubLink[];\n}\n\n/**\n * Lista i repository con il conteggio dei link e la data dell'ultimo evento.\n * Utile per la UI di riepilogo dei repo monitorati.\n */\nexport function listReposWithLinkCount(db: Database): RepoLinkCount[] {\n return db.query(\n `SELECT repo,\n COUNT(*) as count,\n MAX(created_at) as last_event_at\n FROM github_links\n GROUP BY repo\n ORDER BY count DESC, repo ASC`\n ).all() as RepoLinkCount[];\n}\n", "/**\n * Import/Export JSONL per Kiro Memory.\n *\n * Implementa:\n * - Export streaming senza caricare tutto in memoria\n * - Import con deduplicazione SHA256 e batch insert (100 record/batch)\n * - Validazione per ogni riga\n * - Dry-run mode per import\n */\n\nimport { Database } from 'bun:sqlite';\nimport { createHash } from 'crypto';\n\n// \u2500\u2500 Versione schema JSONL \u2500\u2500\nexport const JSONL_SCHEMA_VERSION = '2.5.0';\n\n// \u2500\u2500 Dimensione batch per insert \u2500\u2500\nconst IMPORT_BATCH_SIZE = 100;\n\n// \u2500\u2500 Tipi \u2500\u2500\n\n/** Metadati nel primo record del file JSONL */\nexport interface JsonlMeta {\n _meta: {\n version: string;\n exported_at: string;\n counts: {\n observations?: number;\n summaries?: number;\n prompts?: number;\n };\n filters?: {\n project?: string;\n type?: string;\n from?: string;\n to?: string;\n };\n };\n}\n\n/** Tipo discriminante per ogni record JSONL */\nexport type JsonlRecordType = 'observation' | 'summary' | 'prompt';\n\n/** Riga JSONL per una observation */\nexport interface JsonlObservation {\n _type: 'observation';\n id: number;\n memory_session_id: string;\n project: string;\n type: string;\n title: string;\n subtitle: string | null;\n text: string | null;\n narrative: string | null;\n facts: string | null;\n concepts: string | null;\n files_read: string | null;\n files_modified: string | null;\n prompt_number: number;\n content_hash: string | null;\n discovery_tokens: number;\n auto_category: string | null;\n created_at: string;\n created_at_epoch: number;\n}\n\n/** Riga JSONL per un summary */\nexport interface JsonlSummary {\n _type: 'summary';\n id: number;\n session_id: string;\n project: string;\n request: string | null;\n investigated: string | null;\n learned: string | null;\n completed: string | null;\n next_steps: string | null;\n notes: string | null;\n discovery_tokens: number;\n created_at: string;\n created_at_epoch: number;\n}\n\n/** Riga JSONL per un prompt */\nexport interface JsonlPrompt {\n _type: 'prompt';\n id: number;\n content_session_id: string;\n project: string;\n prompt_number: number;\n prompt_text: string;\n created_at: string;\n created_at_epoch: number;\n}\n\n/** Unione di tutti i tipi riga */\nexport type JsonlRecord = JsonlMeta | JsonlObservation | JsonlSummary | JsonlPrompt;\n\n// \u2500\u2500 Filtri export \u2500\u2500\n\nexport interface ExportFilters {\n project?: string;\n type?: string; // solo per observations\n from?: string; // ISO date string\n to?: string; // ISO date string\n}\n\n// \u2500\u2500 Risultato import \u2500\u2500\n\nexport interface ImportResult {\n imported: number;\n skipped: number;\n errors: number;\n total: number;\n errorDetails: Array<{ line: number; error: string }>;\n}\n\n// \u2500\u2500 Export \u2500\u2500\n\n/**\n * Conta i record che verranno esportati con i filtri forniti.\n * Usato per popolare il campo _meta.counts.\n */\nexport function countExportRecords(\n db: Database,\n filters: ExportFilters\n): { observations: number; summaries: number; prompts: number } {\n const { fromEpoch, toEpoch } = filtersToEpoch(filters);\n\n // Costruisce le condizioni SQL\n const obsConds = buildConditions({ project: filters.project, type: filters.type, fromEpoch, toEpoch });\n const sumConds = buildConditions({ project: filters.project, fromEpoch, toEpoch });\n const promptConds = buildConditions({ project: filters.project, fromEpoch, toEpoch });\n\n const obsCount = (db.query(\n `SELECT COUNT(*) as c FROM observations WHERE ${obsConds.where}`\n ).get(...obsConds.params) as { c: number }).c;\n\n const sumCount = (db.query(\n `SELECT COUNT(*) as c FROM summaries WHERE ${sumConds.where}`\n ).get(...sumConds.params) as { c: number }).c;\n\n const promptCount = (db.query(\n `SELECT COUNT(*) as c FROM prompts WHERE ${promptConds.where}`\n ).get(...promptConds.params) as { c: number }).c;\n\n return { observations: obsCount, summaries: sumCount, prompts: promptCount };\n}\n\n/**\n * Genera il record _meta come prima riga del JSONL.\n */\nexport function generateMetaRecord(\n db: Database,\n filters: ExportFilters\n): string {\n const counts = countExportRecords(db, filters);\n const meta: JsonlMeta = {\n _meta: {\n version: JSONL_SCHEMA_VERSION,\n exported_at: new Date().toISOString(),\n counts,\n filters: Object.keys(filters).length > 0 ? filters : undefined,\n }\n };\n return JSON.stringify(meta);\n}\n\n/**\n * Genera le righe JSONL per le observations con i filtri dati.\n * Usa un cursore (offset/limit) per lo streaming senza caricare tutto in memoria.\n * Callback `onRow` viene chiamata per ogni riga JSON.\n */\nexport function exportObservationsStreaming(\n db: Database,\n filters: ExportFilters,\n onRow: (line: string) => void,\n batchSize: number = 200\n): number {\n const { fromEpoch, toEpoch } = filtersToEpoch(filters);\n const conds = buildConditions({ project: filters.project, type: filters.type, fromEpoch, toEpoch });\n\n let offset = 0;\n let total = 0;\n\n while (true) {\n const rows = db.query(\n `SELECT id, memory_session_id, project, type, title, subtitle, text, narrative, facts, concepts,\n files_read, files_modified, prompt_number, content_hash, discovery_tokens, auto_category,\n created_at, created_at_epoch\n FROM observations\n WHERE ${conds.where}\n ORDER BY created_at_epoch ASC, id ASC\n LIMIT ? OFFSET ?`\n ).all(...conds.params, batchSize, offset) as any[];\n\n if (rows.length === 0) break;\n\n for (const row of rows) {\n const record: JsonlObservation = {\n _type: 'observation',\n id: row.id,\n memory_session_id: row.memory_session_id,\n project: row.project,\n type: row.type,\n title: row.title,\n subtitle: row.subtitle,\n text: row.text,\n narrative: row.narrative,\n facts: row.facts,\n concepts: row.concepts,\n files_read: row.files_read,\n files_modified: row.files_modified,\n prompt_number: row.prompt_number,\n content_hash: row.content_hash,\n discovery_tokens: row.discovery_tokens ?? 0,\n auto_category: row.auto_category,\n created_at: row.created_at,\n created_at_epoch: row.created_at_epoch,\n };\n onRow(JSON.stringify(record));\n total++;\n }\n\n offset += rows.length;\n if (rows.length < batchSize) break;\n }\n\n return total;\n}\n\n/**\n * Genera le righe JSONL per i summaries con i filtri dati.\n */\nexport function exportSummariesStreaming(\n db: Database,\n filters: ExportFilters,\n onRow: (line: string) => void,\n batchSize: number = 200\n): number {\n const { fromEpoch, toEpoch } = filtersToEpoch(filters);\n const conds = buildConditions({ project: filters.project, fromEpoch, toEpoch });\n\n let offset = 0;\n let total = 0;\n\n while (true) {\n const rows = db.query(\n `SELECT id, session_id, project, request, investigated, learned, completed, next_steps, notes,\n discovery_tokens, created_at, created_at_epoch\n FROM summaries\n WHERE ${conds.where}\n ORDER BY created_at_epoch ASC, id ASC\n LIMIT ? OFFSET ?`\n ).all(...conds.params, batchSize, offset) as any[];\n\n if (rows.length === 0) break;\n\n for (const row of rows) {\n const record: JsonlSummary = {\n _type: 'summary',\n id: row.id,\n session_id: row.session_id,\n project: row.project,\n request: row.request,\n investigated: row.investigated,\n learned: row.learned,\n completed: row.completed,\n next_steps: row.next_steps,\n notes: row.notes,\n discovery_tokens: row.discovery_tokens ?? 0,\n created_at: row.created_at,\n created_at_epoch: row.created_at_epoch,\n };\n onRow(JSON.stringify(record));\n total++;\n }\n\n offset += rows.length;\n if (rows.length < batchSize) break;\n }\n\n return total;\n}\n\n/**\n * Genera le righe JSONL per i prompts con i filtri dati.\n */\nexport function exportPromptsStreaming(\n db: Database,\n filters: ExportFilters,\n onRow: (line: string) => void,\n batchSize: number = 200\n): number {\n const { fromEpoch, toEpoch } = filtersToEpoch(filters);\n const conds = buildConditions({ project: filters.project, fromEpoch, toEpoch });\n\n let offset = 0;\n let total = 0;\n\n while (true) {\n const rows = db.query(\n `SELECT id, content_session_id, project, prompt_number, prompt_text, created_at, created_at_epoch\n FROM prompts\n WHERE ${conds.where}\n ORDER BY created_at_epoch ASC, id ASC\n LIMIT ? OFFSET ?`\n ).all(...conds.params, batchSize, offset) as any[];\n\n if (rows.length === 0) break;\n\n for (const row of rows) {\n const record: JsonlPrompt = {\n _type: 'prompt',\n id: row.id,\n content_session_id: row.content_session_id,\n project: row.project,\n prompt_number: row.prompt_number,\n prompt_text: row.prompt_text,\n created_at: row.created_at,\n created_at_epoch: row.created_at_epoch,\n };\n onRow(JSON.stringify(record));\n total++;\n }\n\n offset += rows.length;\n if (rows.length < batchSize) break;\n }\n\n return total;\n}\n\n// \u2500\u2500 Import \u2500\u2500\n\n/**\n * Valida una riga JSONL decodificata.\n * Ritorna null se valida, altrimenti la descrizione dell'errore.\n */\nexport function validateJsonlRow(raw: unknown): string | null {\n if (!raw || typeof raw !== 'object') {\n return 'Il record non \u00E8 un oggetto JSON valido';\n }\n\n const rec = raw as Record<string, unknown>;\n\n // Salta il record _meta\n if ('_meta' in rec) return null;\n\n // Verifica _type\n const validTypes: JsonlRecordType[] = ['observation', 'summary', 'prompt'];\n if (!rec._type || typeof rec._type !== 'string' || !validTypes.includes(rec._type as JsonlRecordType)) {\n return `Campo \"_type\" obbligatorio, uno di: ${validTypes.join(', ')}`;\n }\n\n // Validazione per tipo\n if (rec._type === 'observation') {\n if (!rec.project || typeof rec.project !== 'string') return 'observation: campo \"project\" obbligatorio';\n if (!rec.type || typeof rec.type !== 'string') return 'observation: campo \"type\" obbligatorio';\n if (!rec.title || typeof rec.title !== 'string') return 'observation: campo \"title\" obbligatorio';\n if ((rec.project as string).length > 200) return 'observation: \"project\" troppo lungo (max 200)';\n if ((rec.title as string).length > 500) return 'observation: \"title\" troppo lungo (max 500)';\n } else if (rec._type === 'summary') {\n if (!rec.project || typeof rec.project !== 'string') return 'summary: campo \"project\" obbligatorio';\n if (!rec.session_id || typeof rec.session_id !== 'string') return 'summary: campo \"session_id\" obbligatorio';\n } else if (rec._type === 'prompt') {\n if (!rec.project || typeof rec.project !== 'string') return 'prompt: campo \"project\" obbligatorio';\n if (!rec.content_session_id || typeof rec.content_session_id !== 'string') return 'prompt: campo \"content_session_id\" obbligatorio';\n if (!rec.prompt_text || typeof rec.prompt_text !== 'string') return 'prompt: campo \"prompt_text\" obbligatorio';\n }\n\n return null;\n}\n\n/**\n * Calcola l'hash SHA256 del contenuto di una observation per la deduplicazione.\n * Usa lo stesso schema: project|type|title|narrative (senza sessionId).\n */\nexport function computeImportHash(rec: JsonlObservation): string {\n const payload = [\n rec.project ?? '',\n rec.type ?? '',\n rec.title ?? '',\n rec.narrative ?? '',\n ].join('|');\n return createHash('sha256').update(payload).digest('hex');\n}\n\n/**\n * Verifica se un hash SHA256 esiste gi\u00E0 nelle observations.\n */\nexport function hashExistsInObservations(db: Database, hash: string): boolean {\n const result = db.query(\n 'SELECT id FROM observations WHERE content_hash = ? LIMIT 1'\n ).get(hash) as { id: number } | null;\n return !!result;\n}\n\n/**\n * Importa osservazioni da un array di record pre-parsati in batch da IMPORT_BATCH_SIZE.\n * Skippa i record gi\u00E0 presenti (deduplicazione per hash).\n */\nfunction importObservationBatch(\n db: Database,\n records: JsonlObservation[],\n dryRun: boolean\n): { imported: number; skipped: number } {\n let imported = 0;\n let skipped = 0;\n\n // Raggruppa in batch da IMPORT_BATCH_SIZE\n for (let i = 0; i < records.length; i += IMPORT_BATCH_SIZE) {\n const batch = records.slice(i, i + IMPORT_BATCH_SIZE);\n\n if (dryRun) {\n // In dry-run: conta senza inserire\n for (const rec of batch) {\n const hash = rec.content_hash || computeImportHash(rec);\n if (hashExistsInObservations(db, hash)) {\n skipped++;\n } else {\n imported++;\n }\n }\n continue;\n }\n\n // Insert transazionale per il batch\n const insertBatch = db.transaction(() => {\n for (const rec of batch) {\n const hash = rec.content_hash || computeImportHash(rec);\n\n // Deduplicazione: salta se esiste gi\u00E0\n if (hashExistsInObservations(db, hash)) {\n skipped++;\n continue;\n }\n\n const now = new Date().toISOString();\n db.run(\n `INSERT INTO observations\n (memory_session_id, project, type, title, subtitle, text, narrative, facts, concepts,\n files_read, files_modified, prompt_number, content_hash, discovery_tokens, auto_category,\n created_at, created_at_epoch)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n rec.memory_session_id || 'imported',\n rec.project,\n rec.type,\n rec.title,\n rec.subtitle ?? null,\n rec.text ?? null,\n rec.narrative ?? null,\n rec.facts ?? null,\n rec.concepts ?? null,\n rec.files_read ?? null,\n rec.files_modified ?? null,\n rec.prompt_number ?? 0,\n hash,\n rec.discovery_tokens ?? 0,\n rec.auto_category ?? null,\n rec.created_at || now,\n rec.created_at_epoch || Date.now(),\n ]\n );\n imported++;\n }\n });\n\n insertBatch();\n }\n\n return { imported, skipped };\n}\n\n/**\n * Importa summaries da un array di record in batch da IMPORT_BATCH_SIZE.\n * Deduplicazione per (session_id, project, created_at_epoch).\n */\nfunction importSummaryBatch(\n db: Database,\n records: JsonlSummary[],\n dryRun: boolean\n): { imported: number; skipped: number } {\n let imported = 0;\n let skipped = 0;\n\n for (let i = 0; i < records.length; i += IMPORT_BATCH_SIZE) {\n const batch = records.slice(i, i + IMPORT_BATCH_SIZE);\n\n if (dryRun) {\n for (const rec of batch) {\n const exists = db.query(\n 'SELECT id FROM summaries WHERE session_id = ? AND project = ? AND created_at_epoch = ? LIMIT 1'\n ).get(rec.session_id, rec.project, rec.created_at_epoch ?? 0) as { id: number } | null;\n\n if (exists) skipped++; else imported++;\n }\n continue;\n }\n\n const insertBatch = db.transaction(() => {\n for (const rec of batch) {\n // Deduplicazione per (session_id, project, epoch)\n const exists = db.query(\n 'SELECT id FROM summaries WHERE session_id = ? AND project = ? AND created_at_epoch = ? LIMIT 1'\n ).get(rec.session_id, rec.project, rec.created_at_epoch ?? 0) as { id: number } | null;\n\n if (exists) { skipped++; continue; }\n\n const now = new Date().toISOString();\n db.run(\n `INSERT INTO summaries\n (session_id, project, request, investigated, learned, completed, next_steps, notes,\n discovery_tokens, created_at, created_at_epoch)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n rec.session_id,\n rec.project,\n rec.request ?? null,\n rec.investigated ?? null,\n rec.learned ?? null,\n rec.completed ?? null,\n rec.next_steps ?? null,\n rec.notes ?? null,\n rec.discovery_tokens ?? 0,\n rec.created_at || now,\n rec.created_at_epoch || Date.now(),\n ]\n );\n imported++;\n }\n });\n\n insertBatch();\n }\n\n return { imported, skipped };\n}\n\n/**\n * Importa prompts da un array di record in batch da IMPORT_BATCH_SIZE.\n * Deduplicazione per (content_session_id, prompt_number).\n */\nfunction importPromptBatch(\n db: Database,\n records: JsonlPrompt[],\n dryRun: boolean\n): { imported: number; skipped: number } {\n let imported = 0;\n let skipped = 0;\n\n for (let i = 0; i < records.length; i += IMPORT_BATCH_SIZE) {\n const batch = records.slice(i, i + IMPORT_BATCH_SIZE);\n\n if (dryRun) {\n for (const rec of batch) {\n const exists = db.query(\n 'SELECT id FROM prompts WHERE content_session_id = ? AND prompt_number = ? LIMIT 1'\n ).get(rec.content_session_id, rec.prompt_number ?? 0) as { id: number } | null;\n\n if (exists) skipped++; else imported++;\n }\n continue;\n }\n\n const insertBatch = db.transaction(() => {\n for (const rec of batch) {\n const exists = db.query(\n 'SELECT id FROM prompts WHERE content_session_id = ? AND prompt_number = ? LIMIT 1'\n ).get(rec.content_session_id, rec.prompt_number ?? 0) as { id: number } | null;\n\n if (exists) { skipped++; continue; }\n\n const now = new Date().toISOString();\n db.run(\n `INSERT INTO prompts\n (content_session_id, project, prompt_number, prompt_text, created_at, created_at_epoch)\n VALUES (?, ?, ?, ?, ?, ?)`,\n [\n rec.content_session_id,\n rec.project,\n rec.prompt_number ?? 0,\n rec.prompt_text,\n rec.created_at || now,\n rec.created_at_epoch || Date.now(),\n ]\n );\n imported++;\n }\n });\n\n insertBatch();\n }\n\n return { imported, skipped };\n}\n\n/**\n * Importa un file JSONL completo dal contenuto stringa.\n * Analizza riga per riga, valida, raggruppa per tipo, inserisce in batch.\n *\n * @param db - Istanza del database\n * @param content - Contenuto JSONL completo come stringa\n * @param dryRun - Se true, mostra i conteggi senza inserire\n */\nexport function importJsonl(\n db: Database,\n content: string,\n dryRun: boolean = false\n): ImportResult {\n const lines = content.split('\\n');\n const result: ImportResult = {\n imported: 0,\n skipped: 0,\n errors: 0,\n total: 0,\n errorDetails: [],\n };\n\n // Buffer per batch insert per tipo\n const obsBuf: JsonlObservation[] = [];\n const sumBuf: JsonlSummary[] = [];\n const promptBuf: JsonlPrompt[] = [];\n\n // Funzione di flush dei buffer\n const flushBuffers = () => {\n if (obsBuf.length > 0) {\n const r = importObservationBatch(db, obsBuf.splice(0), dryRun);\n result.imported += r.imported;\n result.skipped += r.skipped;\n }\n if (sumBuf.length > 0) {\n const r = importSummaryBatch(db, sumBuf.splice(0), dryRun);\n result.imported += r.imported;\n result.skipped += r.skipped;\n }\n if (promptBuf.length > 0) {\n const r = importPromptBatch(db, promptBuf.splice(0), dryRun);\n result.imported += r.imported;\n result.skipped += r.skipped;\n }\n };\n\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i].trim();\n\n // Salta righe vuote e commenti\n if (!raw || raw.startsWith('#')) continue;\n\n result.total++;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n result.errors++;\n result.errorDetails.push({ line: i + 1, error: `JSON non valido: ${raw.substring(0, 60)}` });\n continue;\n }\n\n // Salta il record _meta senza contarlo nel totale\n if (parsed && typeof parsed === 'object' && '_meta' in (parsed as object)) {\n result.total--; // annulla l'incremento precedente\n continue;\n }\n\n const validErr = validateJsonlRow(parsed);\n if (validErr) {\n result.errors++;\n result.errorDetails.push({ line: i + 1, error: validErr });\n continue;\n }\n\n const rec = parsed as (JsonlObservation | JsonlSummary | JsonlPrompt);\n\n // Accoda nel buffer del tipo corretto\n if (rec._type === 'observation') {\n obsBuf.push(rec as JsonlObservation);\n } else if (rec._type === 'summary') {\n sumBuf.push(rec as JsonlSummary);\n } else if (rec._type === 'prompt') {\n promptBuf.push(rec as JsonlPrompt);\n }\n\n // Flush automatico quando i buffer raggiungono la dimensione massima\n const totalBuf = obsBuf.length + sumBuf.length + promptBuf.length;\n if (totalBuf >= IMPORT_BATCH_SIZE) {\n flushBuffers();\n }\n }\n\n // Flush finale dei record rimasti nei buffer\n flushBuffers();\n\n return result;\n}\n\n// \u2500\u2500 Utility interne \u2500\u2500\n\n/** Converte i filtri da/a in epoch ms */\nfunction filtersToEpoch(filters: ExportFilters): { fromEpoch?: number; toEpoch?: number } {\n return {\n fromEpoch: filters.from ? new Date(filters.from).getTime() : undefined,\n toEpoch: filters.to ? new Date(filters.to).getTime() : undefined,\n };\n}\n\ninterface ConditionParams {\n project?: string;\n type?: string;\n fromEpoch?: number;\n toEpoch?: number;\n}\n\n/** Costruisce la clausola WHERE e i parametri per le query di export */\nfunction buildConditions(params: ConditionParams): { where: string; params: (string | number)[] } {\n const conditions: string[] = ['1=1'];\n const values: (string | number)[] = [];\n\n if (params.project) {\n conditions.push('project = ?');\n values.push(params.project);\n }\n if (params.type) {\n conditions.push('type = ?');\n values.push(params.type);\n }\n if (params.fromEpoch !== undefined) {\n conditions.push('created_at_epoch >= ?');\n values.push(params.fromEpoch);\n }\n if (params.toEpoch !== undefined) {\n conditions.push('created_at_epoch <= ?');\n values.push(params.toEpoch);\n }\n\n return { where: conditions.join(' AND '), params: values };\n}\n", "/**\n * Kiro Memory SDK for Kiro CLI Integration\n *\n * Provides programmatic access to Kiro Memory system\n */\n\nimport { KiroMemoryDatabase } from '../services/sqlite/index.js';\nimport { encodeCursor, decodeCursor } from '../services/sqlite/cursor.js';\nimport type { KeysetPageResult } from '../services/sqlite/cursor.js';\nimport { getObservationsByProject, createObservation, searchObservations, updateLastAccessed, consolidateObservations as dbConsolidateObservations, isDuplicateObservation } from '../services/sqlite/Observations.js';\nimport { createHash } from 'crypto';\nimport { getSummariesByProject, createSummary, searchSummaries } from '../services/sqlite/Summaries.js';\nimport { getPromptsByProject, createPrompt } from '../services/sqlite/Prompts.js';\nimport { getSessionByContentId, createSession, completeSession as dbCompleteSession } from '../services/sqlite/Sessions.js';\nimport { searchObservationsFTS, searchSummariesFiltered, getObservationsByIds as dbGetObservationsByIds, getTimeline as dbGetTimeline, getStaleObservations as dbGetStaleObservations, markObservationsStale as dbMarkObservationsStale } from '../services/sqlite/Search.js';\nimport { createCheckpoint as dbCreateCheckpoint, getLatestCheckpoint as dbGetLatestCheckpoint, getLatestCheckpointByProject as dbGetLatestCheckpointByProject } from '../services/sqlite/Checkpoints.js';\nimport { getReportData as dbGetReportData } from '../services/sqlite/Reports.js';\nimport { getHybridSearch, type SearchResult } from '../services/search/HybridSearch.js';\nimport { getEmbeddingService } from '../services/search/EmbeddingService.js';\nimport { getVectorSearch } from '../services/search/VectorSearch.js';\nimport { logger } from '../utils/logger.js';\nimport {\n recencyScore,\n projectMatchScore,\n computeCompositeScore,\n knowledgeTypeBoost,\n CONTEXT_WEIGHTS\n} from '../services/search/ScoringEngine.js';\nimport type {\n Observation,\n Summary,\n UserPrompt,\n DBSession,\n DBCheckpoint,\n ContextContext,\n SearchFilters,\n TimelineEntry,\n ScoredItem,\n SmartContext,\n StoreKnowledgeInput,\n KnowledgeType,\n KnowledgeMetadata,\n ReportData\n} from '../types/worker-types.js';\nimport { KNOWLEDGE_TYPES } from '../types/worker-types.js';\n\nexport interface KiroMemoryConfig {\n dataDir?: string;\n project?: string;\n /** Skip migration check for performance (use in high-frequency hooks) */\n skipMigrations?: boolean;\n}\n\nexport class KiroMemorySDK {\n private db: KiroMemoryDatabase;\n private project: string;\n\n constructor(config: KiroMemoryConfig = {}) {\n this.db = new KiroMemoryDatabase(config.dataDir, config.skipMigrations || false);\n this.project = config.project || this.detectProject();\n }\n\n private detectProject(): string {\n try {\n const { execSync } = require('child_process');\n const gitRoot = execSync('git rev-parse --show-toplevel', {\n cwd: process.cwd(),\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'ignore']\n }).trim();\n return gitRoot.split('/').pop() || 'default';\n } catch {\n return 'default';\n }\n }\n\n /**\n * Get context for the current project\n */\n async getContext(): Promise<ContextContext> {\n return {\n project: this.project,\n relevantObservations: getObservationsByProject(this.db.db, this.project, 20),\n relevantSummaries: getSummariesByProject(this.db.db, this.project, 5),\n recentPrompts: getPromptsByProject(this.db.db, this.project, 10)\n };\n }\n\n /**\n * Validate input for storeObservation\n */\n private validateObservationInput(data: { type: string; title: string; content: string }): void {\n if (!data.type || typeof data.type !== 'string' || data.type.length > 100) {\n throw new Error('type is required (string, max 100 chars)');\n }\n if (!data.title || typeof data.title !== 'string' || data.title.length > 500) {\n throw new Error('title is required (string, max 500 chars)');\n }\n if (!data.content || typeof data.content !== 'string' || data.content.length > 100_000) {\n throw new Error('content is required (string, max 100KB)');\n }\n }\n\n /**\n * Validate input for storeSummary\n */\n private validateSummaryInput(data: Record<string, unknown>): void {\n const MAX = 50_000;\n for (const [key, val] of Object.entries(data)) {\n if (val !== undefined && val !== null) {\n if (typeof val !== 'string') throw new Error(`${key} must be a string`);\n if (val.length > MAX) throw new Error(`${key} too large (max 50KB)`);\n }\n }\n }\n\n /**\n * Generate and store embedding for an observation (fire-and-forget, non-blocking)\n */\n private async generateEmbeddingAsync(observationId: number, title: string, content: string, concepts?: string[]): Promise<void> {\n try {\n const embeddingService = getEmbeddingService();\n if (!embeddingService.isAvailable()) return;\n\n // Compose text for embedding: title + content + concepts\n const parts = [title, content];\n if (concepts?.length) parts.push(concepts.join(', '));\n const fullText = parts.join(' ').substring(0, 2000);\n\n const embedding = await embeddingService.embed(fullText);\n if (embedding) {\n const vectorSearch = getVectorSearch();\n await vectorSearch.storeEmbedding(\n this.db.db,\n observationId,\n embedding,\n embeddingService.getProvider() || 'unknown'\n );\n }\n } catch (error) {\n // Don't propagate errors \u2014 embedding is optional\n logger.debug('SDK', `Embedding generation failed for obs ${observationId}: ${error}`);\n }\n }\n\n /**\n * Generate SHA256 content hash for content-based deduplication.\n * Uses (project + type + title + narrative) as semantic identity tuple.\n * Does NOT include sessionId since it's unique per invocation.\n */\n private generateContentHash(type: string, title: string, narrative?: string): string {\n const payload = `${this.project}|${type}|${title}|${narrative || ''}`;\n return createHash('sha256').update(payload).digest('hex');\n }\n\n /**\n * Deduplication windows per type (ms).\n * Types with many repetitions have wider windows.\n */\n private getDeduplicationWindow(type: string): number {\n switch (type) {\n case 'file-read': return 60_000; // 60s \u2014 frequent reads on the same files\n case 'file-write': return 10_000; // 10s \u2014 rapid consecutive writes\n case 'command': return 30_000; // 30s \u2014 standard\n case 'research': return 120_000; // 120s \u2014 repeated web search and fetch\n case 'delegation': return 60_000; // 60s \u2014 rapid delegations\n default: return 30_000; // 30s \u2014 default\n }\n }\n\n /**\n * Store a new observation\n */\n async storeObservation(data: {\n type: string;\n title: string;\n content: string;\n subtitle?: string;\n narrative?: string;\n facts?: string;\n concepts?: string[];\n /** @deprecated Use filesRead/filesModified to separate files */\n files?: string[];\n filesRead?: string[];\n filesModified?: string[];\n }): Promise<number> {\n this.validateObservationInput(data);\n\n const sessionId = 'sdk-' + Date.now();\n\n // Deduplication with content hash (type-specific window)\n const contentHash = this.generateContentHash(data.type, data.title, data.narrative);\n const dedupWindow = this.getDeduplicationWindow(data.type);\n if (isDuplicateObservation(this.db.db, contentHash, dedupWindow)) {\n logger.debug('SDK', `Duplicate observation discarded (${data.type}, ${dedupWindow}ms): ${data.title}`);\n return -1;\n }\n\n // Separate filesRead and filesModified (backward-compatible with generic files)\n const filesRead = data.filesRead || (data.type === 'file-read' ? data.files : undefined);\n const filesModified = data.filesModified || (data.type === 'file-write' ? data.files : undefined);\n\n // Token economics: estimate discovery cost (full content / 4 chars per token)\n const discoveryTokens = Math.ceil(data.content.length / 4);\n\n const observationId = createObservation(\n this.db.db,\n sessionId,\n this.project,\n data.type,\n data.title,\n data.subtitle || null,\n data.content,\n data.narrative || null,\n data.facts || null,\n data.concepts?.join(', ') || null,\n filesRead?.join(', ') || null,\n filesModified?.join(', ') || null,\n 0,\n contentHash,\n discoveryTokens\n );\n\n // Generate embedding in background (fire-and-forget, non-blocking)\n this.generateEmbeddingAsync(observationId, data.title, data.content, data.concepts)\n .catch(() => {}); // Silently ignore errors\n\n return observationId;\n }\n\n /**\n * Store structured knowledge (constraint, decision, heuristic, rejected).\n * Uses the `type` field for knowledgeType and `facts` for JSON metadata.\n */\n async storeKnowledge(data: StoreKnowledgeInput): Promise<number> {\n // Validate knowledgeType against enum\n if (!KNOWLEDGE_TYPES.includes(data.knowledgeType)) {\n throw new Error(`Invalid knowledgeType: ${data.knowledgeType}. Allowed values: ${KNOWLEDGE_TYPES.join(', ')}`);\n }\n this.validateObservationInput({ type: data.knowledgeType, title: data.title, content: data.content });\n\n // Build JSON metadata based on type\n const metadata: KnowledgeMetadata = (() => {\n switch (data.knowledgeType) {\n case 'constraint':\n return {\n knowledgeType: 'constraint' as const,\n severity: data.metadata?.severity || 'soft',\n reason: data.metadata?.reason\n };\n case 'decision':\n return {\n knowledgeType: 'decision' as const,\n alternatives: data.metadata?.alternatives,\n reason: data.metadata?.reason\n };\n case 'heuristic':\n return {\n knowledgeType: 'heuristic' as const,\n context: data.metadata?.context,\n confidence: data.metadata?.confidence\n };\n case 'rejected':\n return {\n knowledgeType: 'rejected' as const,\n reason: data.metadata?.reason || '',\n alternatives: data.metadata?.alternatives\n };\n }\n })();\n\n const sessionId = 'sdk-' + Date.now();\n const contentHash = this.generateContentHash(data.knowledgeType, data.title);\n if (isDuplicateObservation(this.db.db, contentHash)) {\n logger.debug('SDK', `Duplicate knowledge discarded: ${data.title}`);\n return -1;\n }\n\n const discoveryTokens = Math.ceil(data.content.length / 4);\n\n const observationId = createObservation(\n this.db.db,\n sessionId,\n data.project || this.project,\n data.knowledgeType, // type = knowledgeType\n data.title,\n null, // subtitle\n data.content,\n null, // narrative\n JSON.stringify(metadata), // facts = JSON metadata\n data.concepts?.join(', ') || null,\n data.files?.join(', ') || null,\n null, // filesModified: knowledge doesn't modify files\n 0, // prompt_number\n contentHash,\n discoveryTokens\n );\n\n // Generate embedding in background\n this.generateEmbeddingAsync(observationId, data.title, data.content, data.concepts)\n .catch(() => {});\n\n return observationId;\n }\n\n /**\n * Store a session summary\n */\n async storeSummary(data: {\n request?: string;\n investigated?: string;\n learned?: string;\n completed?: string;\n nextSteps?: string;\n notes?: string;\n }): Promise<number> {\n this.validateSummaryInput(data);\n return createSummary(\n this.db.db,\n 'sdk-' + Date.now(),\n this.project,\n data.request || null,\n data.investigated || null,\n data.learned || null,\n data.completed || null,\n data.nextSteps || null,\n data.notes || null\n );\n }\n\n /**\n * Search across all stored context\n */\n async search(query: string): Promise<{\n observations: Observation[];\n summaries: Summary[];\n }> {\n return {\n observations: searchObservations(this.db.db, query, this.project),\n summaries: searchSummaries(this.db.db, query, this.project)\n };\n }\n\n /**\n * Get recent observations\n */\n async getRecentObservations(limit: number = 10): Promise<Observation[]> {\n return getObservationsByProject(this.db.db, this.project, limit);\n }\n\n /**\n * Get recent summaries\n */\n async getRecentSummaries(limit: number = 5): Promise<Summary[]> {\n return getSummariesByProject(this.db.db, this.project, limit);\n }\n\n /**\n * Advanced search with FTS5 and filters\n */\n async searchAdvanced(query: string, filters: SearchFilters = {}): Promise<{\n observations: Observation[];\n summaries: Summary[];\n }> {\n const projectFilters = { ...filters, project: filters.project || this.project };\n\n return {\n observations: searchObservationsFTS(this.db.db, query, projectFilters),\n summaries: searchSummariesFiltered(this.db.db, query, projectFilters)\n };\n }\n\n /**\n * Retrieve observations by ID (batch)\n */\n async getObservationsByIds(ids: number[]): Promise<Observation[]> {\n return dbGetObservationsByIds(this.db.db, ids);\n }\n\n /**\n * Timeline: chronological context around an observation\n */\n async getTimeline(anchorId: number, depthBefore: number = 5, depthAfter: number = 5): Promise<TimelineEntry[]> {\n return dbGetTimeline(this.db.db, anchorId, depthBefore, depthAfter);\n }\n\n /**\n * Create or retrieve a session for the current project\n */\n async getOrCreateSession(contentSessionId: string): Promise<DBSession> {\n let session = getSessionByContentId(this.db.db, contentSessionId);\n if (!session) {\n const id = createSession(this.db.db, contentSessionId, this.project, '');\n session = {\n id, content_session_id: contentSessionId, project: this.project,\n user_prompt: '', memory_session_id: null, status: 'active',\n started_at: new Date().toISOString(), started_at_epoch: Date.now(),\n completed_at: null, completed_at_epoch: null\n };\n }\n return session;\n }\n\n /**\n * Store a user prompt\n */\n async storePrompt(contentSessionId: string, promptNumber: number, text: string): Promise<number> {\n return createPrompt(this.db.db, contentSessionId, this.project, promptNumber, text);\n }\n\n /**\n * Complete a session\n */\n async completeSession(sessionId: number): Promise<void> {\n dbCompleteSession(this.db.db, sessionId);\n }\n\n /**\n * Getter for current project name\n */\n getProject(): string {\n return this.project;\n }\n\n /**\n * Hybrid search: vector search + keyword FTS5\n * Requires HybridSearch initialization (embedding service)\n */\n async hybridSearch(query: string, options: { limit?: number } = {}): Promise<SearchResult[]> {\n const hybridSearch = getHybridSearch();\n return hybridSearch.search(this.db.db, query, {\n project: this.project,\n limit: options.limit || 10\n });\n }\n\n /**\n * Semantic-only search (vector search)\n * Returns results based on cosine similarity with embeddings\n */\n async semanticSearch(query: string, options: { limit?: number; threshold?: number } = {}): Promise<SearchResult[]> {\n const embeddingService = getEmbeddingService();\n if (!embeddingService.isAvailable()) {\n await embeddingService.initialize();\n }\n if (!embeddingService.isAvailable()) return [];\n\n const queryEmbedding = await embeddingService.embed(query);\n if (!queryEmbedding) return [];\n\n const vectorSearch = getVectorSearch();\n const results = await vectorSearch.search(this.db.db, queryEmbedding, {\n project: this.project,\n limit: options.limit || 10,\n threshold: options.threshold || 0.3\n });\n\n return results.map(r => ({\n id: String(r.observationId),\n title: r.title,\n content: r.text || '',\n type: r.type,\n project: r.project,\n created_at: r.created_at,\n created_at_epoch: r.created_at_epoch,\n score: r.similarity,\n source: 'vector' as const,\n signals: {\n semantic: r.similarity,\n fts5: 0,\n recency: recencyScore(r.created_at_epoch),\n projectMatch: projectMatchScore(r.project, this.project)\n }\n }));\n }\n\n /**\n * Generate embeddings for observations that don't have them yet\n */\n async backfillEmbeddings(batchSize: number = 50): Promise<number> {\n const vectorSearch = getVectorSearch();\n return vectorSearch.backfillEmbeddings(this.db.db, batchSize);\n }\n\n /**\n * Embedding statistics in the database\n */\n getEmbeddingStats(): { total: number; embedded: number; percentage: number } {\n const vectorSearch = getVectorSearch();\n return vectorSearch.getStats(this.db.db);\n }\n\n /**\n * Initialize the embedding service (lazy, call before hybridSearch)\n */\n async initializeEmbeddings(): Promise<boolean> {\n const hybridSearch = getHybridSearch();\n await hybridSearch.initialize();\n return getEmbeddingService().isAvailable();\n }\n\n /**\n * Smart context with 4-signal ranking and token budget.\n *\n * If query present: uses HybridSearch with SEARCH_WEIGHTS.\n * If no query: ranking by recency + project match (CONTEXT_WEIGHTS).\n */\n async getSmartContext(options: {\n tokenBudget?: number;\n query?: string;\n } = {}): Promise<SmartContext> {\n const tokenBudget = options.tokenBudget\n || parseInt(process.env.KIRO_MEMORY_CONTEXT_TOKENS || '0', 10)\n || 2000;\n\n // Summaries always included\n const summaries = getSummariesByProject(this.db.db, this.project, 5);\n\n let items: ScoredItem[];\n\n if (options.query) {\n // SEARCH mode: use HybridSearch with full scoring\n const hybridSearch = getHybridSearch();\n const results = await hybridSearch.search(this.db.db, options.query, {\n project: this.project,\n limit: 30\n });\n\n items = results.map(r => ({\n id: parseInt(r.id, 10) || 0,\n title: r.title,\n content: r.content,\n type: r.type,\n project: r.project,\n created_at: r.created_at,\n created_at_epoch: r.created_at_epoch,\n score: r.score,\n signals: r.signals\n }));\n } else {\n // CONTEXT mode: ranking by recency + project match\n const observations = getObservationsByProject(this.db.db, this.project, 30);\n\n // Separate knowledge items (prioritized) from normal observations\n const knowledgeTypes = new Set(KNOWLEDGE_TYPES as readonly string[]);\n const knowledgeObs: typeof observations = [];\n const normalObs: typeof observations = [];\n for (const obs of observations) {\n if (knowledgeTypes.has(obs.type)) knowledgeObs.push(obs);\n else normalObs.push(obs);\n }\n\n const scoreObs = (obs: typeof observations[0]) => {\n const signals = {\n semantic: 0,\n fts5: 0,\n recency: recencyScore(obs.created_at_epoch),\n projectMatch: projectMatchScore(obs.project, this.project)\n };\n const baseScore = computeCompositeScore(signals, CONTEXT_WEIGHTS);\n return {\n id: obs.id,\n title: obs.title,\n content: obs.text || obs.narrative || '',\n type: obs.type,\n project: obs.project,\n created_at: obs.created_at,\n created_at_epoch: obs.created_at_epoch,\n score: Math.min(1, baseScore * knowledgeTypeBoost(obs.type)),\n signals\n };\n };\n\n // Knowledge always on top (sorted by score), then normal observations\n const scoredKnowledge = knowledgeObs.map(scoreObs).sort((a, b) => b.score - a.score);\n const scoredNormal = normalObs.map(scoreObs).sort((a, b) => b.score - a.score);\n items = [...scoredKnowledge, ...scoredNormal];\n }\n\n // Truncate to token budget (knowledge items have priority being at the top)\n let tokensUsed = 0;\n const budgetItems: ScoredItem[] = [];\n for (const item of items) {\n const itemTokens = Math.ceil((item.title.length + item.content.length) / 4);\n if (tokensUsed + itemTokens > tokenBudget) break;\n tokensUsed += itemTokens;\n budgetItems.push(item);\n }\n items = budgetItems;\n\n return {\n project: this.project,\n items,\n summaries,\n tokenBudget,\n tokensUsed: Math.min(tokensUsed, tokenBudget)\n };\n }\n\n /**\n * Detect stale observations (files modified after creation) and mark them in DB.\n * Returns the number of observations marked as stale.\n */\n async detectStaleObservations(): Promise<number> {\n const staleObs = dbGetStaleObservations(this.db.db, this.project);\n if (staleObs.length > 0) {\n const ids = staleObs.map(o => o.id);\n dbMarkObservationsStale(this.db.db, ids, true);\n }\n return staleObs.length;\n }\n\n /**\n * Consolidate duplicate observations on the same file and type.\n * Groups by (project, type, files_modified), keeps the most recent.\n */\n async consolidateObservations(options: { dryRun?: boolean } = {}): Promise<{ merged: number; removed: number }> {\n return dbConsolidateObservations(this.db.db, this.project, options);\n }\n\n /**\n * Decay statistics: total, stale, never accessed, recently accessed.\n */\n async getDecayStats(): Promise<{\n total: number;\n stale: number;\n neverAccessed: number;\n recentlyAccessed: number;\n }> {\n const total = (this.db.db.query(\n 'SELECT COUNT(*) as count FROM observations WHERE project = ?'\n ).get(this.project) as any)?.count || 0;\n\n const stale = (this.db.db.query(\n 'SELECT COUNT(*) as count FROM observations WHERE project = ? AND is_stale = 1'\n ).get(this.project) as any)?.count || 0;\n\n const neverAccessed = (this.db.db.query(\n 'SELECT COUNT(*) as count FROM observations WHERE project = ? AND last_accessed_epoch IS NULL'\n ).get(this.project) as any)?.count || 0;\n\n // \"Recently accessed\" = last access within the past 48 hours\n const recentThreshold = Date.now() - (48 * 60 * 60 * 1000);\n const recentlyAccessed = (this.db.db.query(\n 'SELECT COUNT(*) as count FROM observations WHERE project = ? AND last_accessed_epoch > ?'\n ).get(this.project, recentThreshold) as any)?.count || 0;\n\n return { total, stale, neverAccessed, recentlyAccessed };\n }\n\n /**\n * Create a structured checkpoint for session resume.\n * Automatically saves a context_snapshot with the last 10 observations.\n */\n async createCheckpoint(sessionId: number, data: {\n task: string;\n progress?: string;\n nextSteps?: string;\n openQuestions?: string;\n relevantFiles?: string[];\n }): Promise<number> {\n // Serialize last 10 session observations as context snapshot\n const recentObs = getObservationsByProject(this.db.db, this.project, 10);\n const contextSnapshot = JSON.stringify(\n recentObs.map(o => ({ id: o.id, type: o.type, title: o.title, text: o.text?.substring(0, 200) }))\n );\n\n return dbCreateCheckpoint(this.db.db, sessionId, this.project, {\n task: data.task,\n progress: data.progress,\n nextSteps: data.nextSteps,\n openQuestions: data.openQuestions,\n relevantFiles: data.relevantFiles?.join(', '),\n contextSnapshot\n });\n }\n\n /**\n * Retrieve the latest checkpoint of a specific session.\n */\n async getCheckpoint(sessionId: number): Promise<DBCheckpoint | null> {\n return dbGetLatestCheckpoint(this.db.db, sessionId);\n }\n\n /**\n * Retrieve the latest checkpoint for the current project.\n * Useful for automatic resume without specifying session ID.\n */\n async getLatestProjectCheckpoint(): Promise<DBCheckpoint | null> {\n return dbGetLatestCheckpointByProject(this.db.db, this.project);\n }\n\n /**\n * Generate an activity report for the current project.\n * Aggregates observations, sessions, summaries and files for a time period.\n */\n async generateReport(options?: {\n period?: 'weekly' | 'monthly';\n startDate?: Date;\n endDate?: Date;\n }): Promise<ReportData> {\n const now = new Date();\n let startEpoch: number;\n let endEpoch: number = now.getTime();\n\n if (options?.startDate && options?.endDate) {\n startEpoch = options.startDate.getTime();\n endEpoch = options.endDate.getTime();\n } else {\n const period = options?.period || 'weekly';\n const daysBack = period === 'monthly' ? 30 : 7;\n startEpoch = endEpoch - (daysBack * 24 * 60 * 60 * 1000);\n }\n\n return dbGetReportData(this.db.db, this.project, startEpoch, endEpoch);\n }\n\n /**\n * Lista osservazioni con keyset pagination.\n * Restituisce un oggetto { data, next_cursor, has_more }.\n *\n * Esempio:\n * const page1 = await sdk.listObservations({ limit: 50 });\n * const page2 = await sdk.listObservations({ cursor: page1.next_cursor });\n */\n async listObservations(options: {\n cursor?: string | null;\n limit?: number;\n project?: string;\n } = {}): Promise<KeysetPageResult<Observation>> {\n const limit = Math.min(Math.max(options.limit ?? 50, 1), 200);\n const project = options.project ?? this.project;\n\n let rows: Observation[];\n\n if (options.cursor) {\n // Modalit\u00E0 keyset: decodifica il cursor e applica la WHERE clause\n const decoded = decodeCursor(options.cursor);\n if (!decoded) throw new Error('Cursor non valido');\n\n const sql = project\n ? `SELECT * FROM observations\n WHERE project = ? AND (created_at_epoch < ? OR (created_at_epoch = ? AND id < ?))\n ORDER BY created_at_epoch DESC, id DESC\n LIMIT ?`\n : `SELECT * FROM observations\n WHERE (created_at_epoch < ? OR (created_at_epoch = ? AND id < ?))\n ORDER BY created_at_epoch DESC, id DESC\n LIMIT ?`;\n\n rows = project\n ? this.db.db.query(sql).all(project, decoded.epoch, decoded.epoch, decoded.id, limit) as Observation[]\n : this.db.db.query(sql).all(decoded.epoch, decoded.epoch, decoded.id, limit) as Observation[];\n } else {\n // Prima pagina (nessun cursor)\n const sql = project\n ? 'SELECT * FROM observations WHERE project = ? ORDER BY created_at_epoch DESC, id DESC LIMIT ?'\n : 'SELECT * FROM observations ORDER BY created_at_epoch DESC, id DESC LIMIT ?';\n\n rows = project\n ? this.db.db.query(sql).all(project, limit) as Observation[]\n : this.db.db.query(sql).all(limit) as Observation[];\n }\n\n // Costruisce il cursor per la pagina successiva se la pagina \u00E8 piena\n const next_cursor = rows.length >= limit\n ? encodeCursor(rows[rows.length - 1].id, rows[rows.length - 1].created_at_epoch)\n : null;\n\n return { data: rows, next_cursor, has_more: next_cursor !== null };\n }\n\n /**\n * Lista sommari con keyset pagination.\n * Restituisce un oggetto { data, next_cursor, has_more }.\n *\n * Esempio:\n * const page1 = await sdk.listSummaries({ limit: 20 });\n * const page2 = await sdk.listSummaries({ cursor: page1.next_cursor });\n */\n async listSummaries(options: {\n cursor?: string | null;\n limit?: number;\n project?: string;\n } = {}): Promise<KeysetPageResult<Summary>> {\n const limit = Math.min(Math.max(options.limit ?? 20, 1), 200);\n const project = options.project ?? this.project;\n\n let rows: Summary[];\n\n if (options.cursor) {\n // Modalit\u00E0 keyset: decodifica il cursor e applica la WHERE clause\n const decoded = decodeCursor(options.cursor);\n if (!decoded) throw new Error('Cursor non valido');\n\n const sql = project\n ? `SELECT * FROM summaries\n WHERE project = ? AND (created_at_epoch < ? OR (created_at_epoch = ? AND id < ?))\n ORDER BY created_at_epoch DESC, id DESC\n LIMIT ?`\n : `SELECT * FROM summaries\n WHERE (created_at_epoch < ? OR (created_at_epoch = ? AND id < ?))\n ORDER BY created_at_epoch DESC, id DESC\n LIMIT ?`;\n\n rows = project\n ? this.db.db.query(sql).all(project, decoded.epoch, decoded.epoch, decoded.id, limit) as Summary[]\n : this.db.db.query(sql).all(decoded.epoch, decoded.epoch, decoded.id, limit) as Summary[];\n } else {\n // Prima pagina (nessun cursor)\n const sql = project\n ? 'SELECT * FROM summaries WHERE project = ? ORDER BY created_at_epoch DESC, id DESC LIMIT ?'\n : 'SELECT * FROM summaries ORDER BY created_at_epoch DESC, id DESC LIMIT ?';\n\n rows = project\n ? this.db.db.query(sql).all(project, limit) as Summary[]\n : this.db.db.query(sql).all(limit) as Summary[];\n }\n\n // Costruisce il cursor per la pagina successiva se la pagina \u00E8 piena\n const next_cursor = rows.length >= limit\n ? encodeCursor(rows[rows.length - 1].id, rows[rows.length - 1].created_at_epoch)\n : null;\n\n return { data: rows, next_cursor, has_more: next_cursor !== null };\n }\n\n /**\n * Getter for direct database access (for API routes)\n */\n getDb(): any {\n return this.db.db;\n }\n\n /**\n * Close database connection\n */\n close(): void {\n this.db.close();\n }\n}\n\n// Export convenience function\nexport function createKiroMemory(config?: KiroMemoryConfig): KiroMemorySDK {\n return new KiroMemorySDK(config);\n}\n\n// Re-export types\nexport type {\n Observation,\n Summary,\n UserPrompt,\n DBSession,\n DBCheckpoint,\n ContextContext,\n SearchFilters,\n TimelineEntry,\n ScoredItem,\n SmartContext,\n ScoringWeights,\n StoreKnowledgeInput,\n KnowledgeType,\n KnowledgeMetadata,\n ReportData\n} from '../types/worker-types.js';\nexport { KNOWLEDGE_TYPES } from '../types/worker-types.js';\n\nexport type { SearchResult } from '../services/search/HybridSearch.js';\nexport type { KeysetPageResult } from '../services/sqlite/cursor.js';\n", "/**\n * Kiro Memory - Persistent cross-session memory for Kiro CLI\n *\n * @packageDocumentation\n */\n\n// Export SDK\nexport { KiroMemorySDK, createKiroMemory } from './sdk/index.js';\nexport type { KiroMemoryConfig } from './sdk/index.js';\n\n// Export database\nexport { KiroMemoryDatabase } from './services/sqlite/index.js';\n\n// Export advanced search\nexport {\n searchObservationsFTS,\n searchObservationsLIKE,\n searchSummariesFiltered,\n getObservationsByIds,\n getTimeline,\n getProjectStats\n} from './services/sqlite/Search.js';\n\n// Export types\nexport type {\n Observation,\n Summary,\n UserPrompt,\n DBSession,\n ContextContext,\n KiroMessage,\n KiroSession,\n KiroHookInput,\n SearchFilters,\n SearchResult,\n TimelineEntry\n} from './types/worker-types.js';\n\n// Export shared hook utilities\nexport { readStdin, detectProject, formatContext, runHook } from './hooks/utils.js';\n\n// Export utilities\nexport { logger, LogLevel } from './utils/logger.js';\nexport type { Component } from './utils/logger.js';\n\n// Version\nexport const VERSION = '3.0.0';\n", "/**\n * Shared utilities for Kiro CLI hooks\n *\n * Kiro contract:\n * - Input: JSON via stdin with { hook_event_name, cwd, tool_name, tool_input, tool_response }\n * - Output: text on stdout (injected into agent context)\n * - Exit code 0 = success, 2 = block (stderr sent to LLM)\n */\n\nimport { writeFileSync, mkdirSync, existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport type { KiroHookInput, ScoredItem, Summary } from '../types/worker-types.js';\nimport { estimateTokens } from '../services/search/ScoringEngine.js';\n\n// Path to shared authentication token with the worker\nconst DATA_DIR = process.env.KIRO_MEMORY_DATA_DIR\n || process.env.CONTEXTKIT_DATA_DIR\n || join(process.env.HOME || '/tmp', '.kiro-memory');\nconst TOKEN_FILE = join(DATA_DIR, 'worker.token');\n\n/**\n * Logging di debug per gli hook (attivo solo con KIRO_MEMORY_LOG_LEVEL=DEBUG)\n */\nexport function debugLog(hookName: string, label: string, data: unknown): void {\n if ((process.env.KIRO_MEMORY_LOG_LEVEL || '').toUpperCase() !== 'DEBUG') return;\n try {\n const dataDir = process.env.KIRO_MEMORY_DATA_DIR\n || join(process.env.HOME || '/tmp', '.kiro-memory');\n const logDir = join(dataDir, 'logs');\n if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });\n\n const ts = new Date().toISOString();\n const line = `[${ts}] [${hookName}] ${label}: ${JSON.stringify(data)}\\n`;\n const logFile = join(logDir, `hooks-${new Date().toISOString().split('T')[0]}.log`);\n writeFileSync(logFile, line, { flag: 'a' });\n } catch {\n // Logging must never block the hook\n }\n}\n\n/**\n * Read and parse JSON from stdin\n */\nexport async function readStdin(): Promise<KiroHookInput> {\n return new Promise((resolve, reject) => {\n let data = '';\n\n process.stdin.setEncoding('utf8');\n // Safety timeout: 5 seconds (cleared in end/error to prevent leaks)\n const safetyTimeout = setTimeout(() => {\n if (!data.trim()) {\n resolve({\n hook_event_name: 'agentSpawn',\n cwd: process.cwd()\n });\n }\n }, 5000);\n\n process.stdin.on('data', (chunk) => { data += chunk; });\n process.stdin.on('end', () => {\n clearTimeout(safetyTimeout);\n try {\n if (!data.trim()) {\n resolve({\n hook_event_name: 'agentSpawn',\n cwd: process.cwd()\n });\n return;\n }\n resolve(JSON.parse(data));\n } catch (err) {\n reject(new Error(`Error parsing stdin JSON: ${err}`));\n }\n });\n process.stdin.on('error', (err) => {\n clearTimeout(safetyTimeout);\n reject(err);\n });\n });\n}\n\n/**\n * Detect project name from cwd\n */\nexport function detectProject(cwd: string): string {\n try {\n const { execSync } = require('child_process');\n const gitRoot = execSync('git rev-parse --show-toplevel', {\n cwd,\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'ignore']\n }).trim();\n return gitRoot.split('/').pop() || 'default';\n } catch {\n // Fallback: last path segment\n return cwd.split('/').pop() || 'default';\n }\n}\n\n/**\n * Format context for injection into Kiro\n */\nexport function formatContext(data: {\n observations?: Array<{ title: string; text?: string | null; type?: string; created_at?: string }>;\n summaries?: Array<{ learned?: string | null; completed?: string | null; next_steps?: string | null; created_at?: string }>;\n prompts?: Array<{ prompt_text: string; created_at?: string }>;\n}): string {\n let output = '';\n\n if (data.summaries && data.summaries.length > 0) {\n output += '## Previous Sessions\\n\\n';\n data.summaries.slice(0, 3).forEach(sum => {\n if (sum.learned) output += `- **Learned**: ${sum.learned}\\n`;\n if (sum.completed) output += `- **Completed**: ${sum.completed}\\n`;\n if (sum.next_steps) output += `- **Next steps**: ${sum.next_steps}\\n`;\n output += '\\n';\n });\n }\n\n if (data.observations && data.observations.length > 0) {\n output += '## Recent Observations\\n\\n';\n data.observations.slice(0, 10).forEach(obs => {\n const text = obs.text ? obs.text.substring(0, 150) : '';\n output += `- **[${obs.type || 'obs'}] ${obs.title}**: ${text}\\n`;\n });\n output += '\\n';\n }\n\n return output;\n}\n\n/**\n * Notify the worker that new data is available.\n * Calls POST /api/notify to trigger SSE broadcast to dashboard clients.\n * Non-blocking: silently ignores if the worker is not running.\n */\nexport async function notifyWorker(event: string, data?: Record<string, unknown>): Promise<void> {\n const host = process.env.KIRO_MEMORY_WORKER_HOST || '127.0.0.1';\n const port = process.env.KIRO_MEMORY_WORKER_PORT || '3001';\n try {\n // Read shared authentication token with the worker\n let token = '';\n try {\n token = readFileSync(TOKEN_FILE, 'utf-8').trim();\n } catch {\n // Token not found: worker might not be running\n return;\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 1500);\n await fetch(`http://${host}:${port}/api/notify`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Worker-Token': token\n },\n body: JSON.stringify({ event, data: data || {} }),\n signal: controller.signal\n });\n clearTimeout(timeout);\n } catch {\n // Worker not running \u2014 silently ignore\n }\n}\n\n/**\n * Format smart context with token budget.\n * Fills the budget with items sorted by descending score.\n * Summaries always included (max 3). Items truncated if needed.\n */\nexport function formatSmartContext(data: {\n items: ScoredItem[];\n summaries: Summary[];\n project: string;\n tokenBudget?: number;\n}): string {\n const budget = data.tokenBudget\n || parseInt(process.env.KIRO_MEMORY_CONTEXT_TOKENS || '0', 10)\n || 2000;\n\n let output = '';\n let tokensUsed = 0;\n\n // Header\n const header = '# Kiro Memory: Previous Sessions Context\\n\\n';\n tokensUsed += estimateTokens(header);\n output += header;\n\n // Summaries (always included, max 3)\n if (data.summaries && data.summaries.length > 0) {\n let sumSection = '## Previous Sessions\\n\\n';\n for (const sum of data.summaries.slice(0, 3)) {\n if (sum.learned) sumSection += `- **Learned**: ${sum.learned}\\n`;\n if (sum.completed) sumSection += `- **Completed**: ${sum.completed}\\n`;\n if (sum.next_steps) sumSection += `- **Next steps**: ${sum.next_steps}\\n`;\n sumSection += '\\n';\n }\n tokensUsed += estimateTokens(sumSection);\n output += sumSection;\n }\n\n // Observations sorted by score (greedy filling)\n if (data.items && data.items.length > 0) {\n let obsSection = '## Relevant Observations\\n\\n';\n tokensUsed += estimateTokens(obsSection);\n\n // Sort by descending score (should already be sorted, but ensure it)\n const sorted = [...data.items].sort((a, b) => b.score - a.score);\n\n for (const item of sorted) {\n // Base line: type + title\n const linePrefix = `- **[${item.type}] ${item.title}**: `;\n const linePrefixTokens = estimateTokens(linePrefix);\n\n // Remaining budget for content\n const remainingTokens = budget - tokensUsed - linePrefixTokens - 1; // 1 per \\n\n\n if (remainingTokens <= 0) break; // Budget exhausted\n\n // Truncate content to remaining budget\n const maxContentChars = remainingTokens * 4; // 1 token \u2248 4 char\n const content = item.content\n ? item.content.substring(0, Math.min(maxContentChars, 300))\n : '';\n\n const line = `${linePrefix}${content}\\n`;\n tokensUsed += estimateTokens(line);\n obsSection += line;\n\n // If we exceeded the budget, stop\n if (tokensUsed >= budget) break;\n }\n\n output += obsSection;\n }\n\n // Footer with stats\n const footer = `\\n> Project: ${data.project} | Items: ${data.items?.length || 0} | Tokens used: ~${tokensUsed}/${budget}\\n`;\n output += footer;\n\n return output;\n}\n\n/**\n * Safe wrapper to execute a hook with error handling\n */\nexport async function runHook(\n name: string,\n handler: (input: KiroHookInput) => Promise<void>\n): Promise<void> {\n try {\n const input = await readStdin();\n\n // Cross-platform normalization: Cursor uses workspace_roots and conversation_id\n if (!input.cwd && input.workspace_roots?.[0]) {\n input.cwd = input.workspace_roots[0];\n }\n if (!input.session_id && input.conversation_id) {\n input.session_id = input.conversation_id;\n }\n\n debugLog(name, 'stdin', input);\n await handler(input);\n debugLog(name, 'completed', { success: true });\n process.exit(0);\n } catch (error) {\n debugLog(name, 'error', { error: String(error) });\n process.stderr.write(`[kiro-memory:${name}] Error: ${error}\\n`);\n process.exit(0); // Exit 0 for silent degradation (don't block Kiro)\n }\n}\n", "/**\n * Router Observations: CRUD observations, batch, knowledge, memory save.\n * Supporta keyset pagination tramite parametro `cursor` (base64 encoded epoch:id).\n * Il parametro `offset` \u00E8 mantenuto come fallback deprecato per backward compatibility.\n */\n\nimport { Router } from 'express';\nimport type { WorkerContext } from '../worker-context.js';\nimport { isValidProject, isValidString, parseIntSafe } from '../worker-context.js';\nimport { getObservationsByProject, createObservation } from '../sqlite/Observations.js';\nimport { getSummariesByProject } from '../sqlite/Summaries.js';\nimport { getObservationsByIds } from '../sqlite/Search.js';\nimport { decodeCursor, buildNextCursor } from '../sqlite/cursor.js';\nimport { KNOWLEDGE_TYPES } from '../../types/worker-types.js';\nimport type { KnowledgeMetadata } from '../../types/worker-types.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createObservationsRouter(ctx: WorkerContext): Router {\n const router = Router();\n\n // Lista osservazioni con keyset pagination.\n // Parametri:\n // cursor \u2014 cursor opaco base64 restituito dall'ultima risposta (prima pagina: assente)\n // limit \u2014 elementi per pagina (default 50, max 200)\n // project \u2014 filtro opzionale per progetto\n // offset \u2014 [DEPRECATO] fallback OFFSET-based per compatibilit\u00E0; ignorato se cursor \u00E8 presente\n router.get('/api/observations', (req, res) => {\n const { cursor, offset, limit, project } = req.query as {\n cursor?: string;\n offset?: string;\n limit?: string;\n project?: string;\n };\n const _limit = parseIntSafe(limit, 50, 1, 200);\n\n try {\n let rows: unknown[];\n\n if (cursor) {\n // Modalit\u00E0 keyset: WHERE (created_at_epoch, id) < (cursorEpoch, cursorId)\n const decoded = decodeCursor(cursor);\n if (!decoded) {\n res.status(400).json({ error: 'Cursor non valido' });\n return;\n }\n\n const sql = project\n ? `SELECT * FROM observations\n WHERE project = ? AND (created_at_epoch < ? OR (created_at_epoch = ? AND id < ?))\n ORDER BY created_at_epoch DESC, id DESC\n LIMIT ?`\n : `SELECT * FROM observations\n WHERE (created_at_epoch < ? OR (created_at_epoch = ? AND id < ?))\n ORDER BY created_at_epoch DESC, id DESC\n LIMIT ?`;\n\n rows = project\n ? ctx.db.db.query(sql).all(project, decoded.epoch, decoded.epoch, decoded.id, _limit)\n : ctx.db.db.query(sql).all(decoded.epoch, decoded.epoch, decoded.id, _limit);\n } else if (offset !== undefined) {\n // Modalit\u00E0 fallback OFFSET (deprecata)\n const _offset = parseIntSafe(offset, 0, 0, 1_000_000);\n const sql = project\n ? 'SELECT * FROM observations WHERE project = ? ORDER BY created_at_epoch DESC, id DESC LIMIT ? OFFSET ?'\n : 'SELECT * FROM observations ORDER BY created_at_epoch DESC, id DESC LIMIT ? OFFSET ?';\n rows = project\n ? ctx.db.db.query(sql).all(project, _limit, _offset)\n : ctx.db.db.query(sql).all(_limit, _offset);\n } else {\n // Prima pagina senza cursor\n const sql = project\n ? 'SELECT * FROM observations WHERE project = ? ORDER BY created_at_epoch DESC, id DESC LIMIT ?'\n : 'SELECT * FROM observations ORDER BY created_at_epoch DESC, id DESC LIMIT ?';\n rows = project\n ? ctx.db.db.query(sql).all(project, _limit)\n : ctx.db.db.query(sql).all(_limit);\n }\n\n // Costruisce il cursor per la pagina successiva\n const typedRows = rows as Array<{ id: number; created_at_epoch: number }>;\n const next_cursor = buildNextCursor(typedRows, _limit);\n\n // Header X-Total-Count per backward compatibility (solo in modalit\u00E0 non-cursor)\n if (!cursor) {\n const countSql = project\n ? 'SELECT COUNT(*) as total FROM observations WHERE project = ?'\n : 'SELECT COUNT(*) as total FROM observations';\n const { total } = (project\n ? ctx.db.db.query(countSql).get(project)\n : ctx.db.db.query(countSql).get()) as { total: number };\n res.setHeader('X-Total-Count', total);\n }\n\n res.json({ data: rows, next_cursor, has_more: next_cursor !== null });\n } catch (error) {\n logger.error('WORKER', 'Observation list failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to list observations' });\n }\n });\n\n // Create observation\n router.post('/api/observations', (req, res) => {\n const { memorySessionId, project, type, title, content, concepts, files } = req.body;\n\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid or missing \"project\"' });\n return;\n }\n if (!isValidString(title, 500)) {\n res.status(400).json({ error: 'Invalid or missing \"title\" (max 500 chars)' });\n return;\n }\n if (content && !isValidString(content, 100_000)) {\n res.status(400).json({ error: '\"content\" too large (max 100KB)' });\n return;\n }\n if (concepts && !Array.isArray(concepts)) {\n res.status(400).json({ error: '\"concepts\" must be an array' });\n return;\n }\n if (files && !Array.isArray(files)) {\n res.status(400).json({ error: '\"files\" must be an array' });\n return;\n }\n\n try {\n const id = createObservation(\n ctx.db.db,\n memorySessionId || 'api-' + Date.now(),\n project,\n type || 'manual',\n title,\n null,\n content,\n null,\n null,\n concepts?.join(', ') || null,\n files?.join(', ') || null,\n null,\n 0\n );\n\n ctx.broadcast('observation-created', { id, project, title });\n ctx.invalidateProjectsCache();\n\n // Background embedding\n ctx.generateEmbeddingForObservation(id, title, content, concepts).catch(() => {});\n\n res.json({ id, success: true });\n } catch (error) {\n logger.error('WORKER', 'Observation creation failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to store observation' });\n }\n });\n\n // Batch fetch observations by ID (max 100)\n router.post('/api/observations/batch', (req, res) => {\n const { ids } = req.body;\n\n if (!ids || !Array.isArray(ids) || ids.length === 0 || ids.length > 100) {\n res.status(400).json({ error: '\"ids\" must be an array of 1-100 elements' });\n return;\n }\n if (!ids.every((id: unknown) => typeof id === 'number' && Number.isInteger(id) && id > 0)) {\n res.status(400).json({ error: 'All IDs must be positive integers' });\n return;\n }\n\n try {\n const observations = getObservationsByIds(ctx.db.db, ids);\n res.json({ observations });\n } catch (error) {\n logger.error('WORKER', 'Batch fetch failed', { ids }, error as Error);\n res.status(500).json({ error: 'Batch fetch failed' });\n }\n });\n\n // Store structured knowledge\n router.post('/api/knowledge', (req, res) => {\n const { project, knowledge_type, title, content, concepts, files,\n severity, alternatives, reason, context: metaContext, confidence } = req.body;\n\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid or missing \"project\"' });\n return;\n }\n if (!knowledge_type || !KNOWLEDGE_TYPES.includes(knowledge_type)) {\n res.status(400).json({ error: `Invalid \"knowledge_type\". Must be one of: ${KNOWLEDGE_TYPES.join(', ')}` });\n return;\n }\n if (!isValidString(title, 500)) {\n res.status(400).json({ error: 'Invalid or missing \"title\" (max 500 chars)' });\n return;\n }\n if (!isValidString(content, 100_000)) {\n res.status(400).json({ error: 'Invalid or missing \"content\" (max 100KB)' });\n return;\n }\n if (concepts && !Array.isArray(concepts)) {\n res.status(400).json({ error: '\"concepts\" must be an array' });\n return;\n }\n if (files && !Array.isArray(files)) {\n res.status(400).json({ error: '\"files\" must be an array' });\n return;\n }\n\n try {\n let metadata: KnowledgeMetadata;\n switch (knowledge_type) {\n case 'constraint':\n metadata = { knowledgeType: 'constraint', severity: severity === 'hard' ? 'hard' : 'soft', reason };\n break;\n case 'decision':\n metadata = { knowledgeType: 'decision', alternatives, reason };\n break;\n case 'heuristic':\n metadata = { knowledgeType: 'heuristic', context: metaContext, confidence: ['high', 'medium', 'low'].includes(confidence) ? confidence : undefined };\n break;\n case 'rejected':\n metadata = { knowledgeType: 'rejected', reason: reason || '', alternatives };\n break;\n default:\n res.status(400).json({ error: 'Invalid knowledge_type' });\n return;\n }\n\n const id = createObservation(\n ctx.db.db,\n 'api-' + Date.now(),\n project,\n knowledge_type,\n title,\n null,\n content,\n null,\n JSON.stringify(metadata),\n concepts?.join(', ') || null,\n files?.join(', ') || null,\n null,\n 0\n );\n\n ctx.broadcast('observation-created', { id, project, title, type: knowledge_type });\n ctx.generateEmbeddingForObservation(id, title, content, concepts).catch(() => {});\n\n res.json({ id, success: true, knowledge_type });\n } catch (error) {\n logger.error('WORKER', 'Knowledge save failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to store knowledge' });\n }\n });\n\n // Save memory (programmable endpoint)\n router.post('/api/memory/save', (req, res) => {\n const { project, title, content, type, concepts } = req.body;\n\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid or missing \"project\"' });\n return;\n }\n if (!isValidString(title, 500)) {\n res.status(400).json({ error: 'Invalid or missing \"title\" (max 500 chars)' });\n return;\n }\n if (!isValidString(content, 100_000)) {\n res.status(400).json({ error: 'Invalid or missing \"content\" (max 100KB)' });\n return;\n }\n\n const obsType = type || 'research';\n const conceptStr = Array.isArray(concepts) ? concepts.join(', ') : (concepts || null);\n\n try {\n const id = createObservation(\n ctx.db.db,\n 'memory-save-' + Date.now(),\n project,\n obsType,\n title,\n null,\n content,\n content,\n null,\n conceptStr,\n null,\n null,\n 0\n );\n\n ctx.broadcast('observation-created', { id, project, title });\n ctx.invalidateProjectsCache();\n ctx.generateEmbeddingForObservation(id, title, content, Array.isArray(concepts) ? concepts : undefined).catch(() => {});\n\n res.json({ id, success: true });\n } catch (error) {\n logger.error('WORKER', 'Memory save failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to save memory' });\n }\n });\n\n // Context by project\n router.get('/api/context/:project', (req, res) => {\n const { project } = req.params;\n\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const context = {\n project,\n observations: getObservationsByProject(ctx.db.db, project, 20),\n summaries: getSummariesByProject(ctx.db.db, project, 5)\n };\n res.json(context);\n } catch (error) {\n logger.error('WORKER', 'Context retrieval failed', { project }, error as Error);\n res.status(500).json({ error: 'Failed to get context' });\n }\n });\n\n return router;\n}\n", "/**\n * Router Summaries: session summary CRUD.\n * Supporta keyset pagination tramite parametro `cursor` (base64 encoded epoch:id).\n * Il parametro `offset` \u00E8 mantenuto come fallback deprecato per backward compatibility.\n */\n\nimport { Router } from 'express';\nimport type { WorkerContext } from '../worker-context.js';\nimport { isValidProject, isValidString, parseIntSafe } from '../worker-context.js';\nimport { createSummary } from '../sqlite/Summaries.js';\nimport { decodeCursor, buildNextCursor } from '../sqlite/cursor.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createSummariesRouter(ctx: WorkerContext): Router {\n const router = Router();\n\n // Lista sommari con keyset pagination.\n // Parametri:\n // cursor \u2014 cursor opaco base64 restituito dall'ultima risposta (prima pagina: assente)\n // limit \u2014 elementi per pagina (default 20, max 200)\n // project \u2014 filtro opzionale per progetto\n // offset \u2014 [DEPRECATO] fallback OFFSET-based per compatibilit\u00E0; ignorato se cursor \u00E8 presente\n router.get('/api/summaries', (req, res) => {\n const { cursor, offset, limit, project } = req.query as {\n cursor?: string;\n offset?: string;\n limit?: string;\n project?: string;\n };\n const _limit = parseIntSafe(limit, 20, 1, 200);\n\n try {\n let rows: unknown[];\n\n if (cursor) {\n // Modalit\u00E0 keyset: WHERE (created_at_epoch, id) < (cursorEpoch, cursorId)\n const decoded = decodeCursor(cursor);\n if (!decoded) {\n res.status(400).json({ error: 'Cursor non valido' });\n return;\n }\n\n const sql = project\n ? `SELECT * FROM summaries\n WHERE project = ? AND (created_at_epoch < ? OR (created_at_epoch = ? AND id < ?))\n ORDER BY created_at_epoch DESC, id DESC\n LIMIT ?`\n : `SELECT * FROM summaries\n WHERE (created_at_epoch < ? OR (created_at_epoch = ? AND id < ?))\n ORDER BY created_at_epoch DESC, id DESC\n LIMIT ?`;\n\n rows = project\n ? ctx.db.db.query(sql).all(project, decoded.epoch, decoded.epoch, decoded.id, _limit)\n : ctx.db.db.query(sql).all(decoded.epoch, decoded.epoch, decoded.id, _limit);\n } else if (offset !== undefined) {\n // Modalit\u00E0 fallback OFFSET (deprecata)\n const _offset = parseIntSafe(offset, 0, 0, 1_000_000);\n const sql = project\n ? 'SELECT * FROM summaries WHERE project = ? ORDER BY created_at_epoch DESC, id DESC LIMIT ? OFFSET ?'\n : 'SELECT * FROM summaries ORDER BY created_at_epoch DESC, id DESC LIMIT ? OFFSET ?';\n rows = project\n ? ctx.db.db.query(sql).all(project, _limit, _offset)\n : ctx.db.db.query(sql).all(_limit, _offset);\n } else {\n // Prima pagina senza cursor\n const sql = project\n ? 'SELECT * FROM summaries WHERE project = ? ORDER BY created_at_epoch DESC, id DESC LIMIT ?'\n : 'SELECT * FROM summaries ORDER BY created_at_epoch DESC, id DESC LIMIT ?';\n rows = project\n ? ctx.db.db.query(sql).all(project, _limit)\n : ctx.db.db.query(sql).all(_limit);\n }\n\n // Costruisce il cursor per la pagina successiva\n const typedRows = rows as Array<{ id: number; created_at_epoch: number }>;\n const next_cursor = buildNextCursor(typedRows, _limit);\n\n // Header X-Total-Count per backward compatibility (solo in modalit\u00E0 non-cursor)\n if (!cursor) {\n const countSql = project\n ? 'SELECT COUNT(*) as total FROM summaries WHERE project = ?'\n : 'SELECT COUNT(*) as total FROM summaries';\n const { total } = (project\n ? ctx.db.db.query(countSql).get(project)\n : ctx.db.db.query(countSql).get()) as { total: number };\n res.setHeader('X-Total-Count', total);\n }\n\n res.json({ data: rows, next_cursor, has_more: next_cursor !== null });\n } catch (error) {\n logger.error('WORKER', 'Summary list failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to list summaries' });\n }\n });\n\n // Create summary\n router.post('/api/summaries', (req, res) => {\n const { sessionId, project, request, learned, completed, nextSteps } = req.body;\n\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid or missing \"project\"' });\n return;\n }\n const MAX_FIELD = 50_000;\n if (request && !isValidString(request, MAX_FIELD)) { res.status(400).json({ error: '\"request\" too large' }); return; }\n if (learned && !isValidString(learned, MAX_FIELD)) { res.status(400).json({ error: '\"learned\" too large' }); return; }\n if (completed && !isValidString(completed, MAX_FIELD)) { res.status(400).json({ error: '\"completed\" too large' }); return; }\n if (nextSteps && !isValidString(nextSteps, MAX_FIELD)) { res.status(400).json({ error: '\"nextSteps\" too large' }); return; }\n\n try {\n const id = createSummary(\n ctx.db.db,\n sessionId || 'api-' + Date.now(),\n project,\n request || null,\n null,\n learned || null,\n completed || null,\n nextSteps || null,\n null\n );\n\n ctx.broadcast('summary-created', { id, project });\n res.json({ id, success: true });\n } catch (error) {\n logger.error('WORKER', 'Summary creation failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to store summary' });\n }\n });\n\n return router;\n}\n", "/**\n * Router Search: FTS5 search, hybrid search, timeline.\n */\n\nimport { Router } from 'express';\nimport type { WorkerContext } from '../worker-context.js';\nimport { parseIntSafe } from '../worker-context.js';\nimport { searchObservationsFTS, searchSummariesFiltered, getTimeline } from '../sqlite/Search.js';\nimport { getHybridSearch } from '../search/HybridSearch.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createSearchRouter(ctx: WorkerContext): Router {\n const router = Router();\n\n // FTS5 search with filters\n router.get('/api/search', (req, res) => {\n const { q, project, type, limit } = req.query as { q: string; project?: string; type?: string; limit?: string };\n\n if (!q) {\n res.status(400).json({ error: 'Query parameter \"q\" is required' });\n return;\n }\n\n try {\n const filters = {\n project: project || undefined,\n type: type || undefined,\n limit: parseIntSafe(limit, 20, 1, 100)\n };\n\n const results = {\n observations: searchObservationsFTS(ctx.db.db, q, filters),\n summaries: searchSummariesFiltered(ctx.db.db, q, filters)\n };\n\n res.json(results);\n } catch (error) {\n logger.error('WORKER', 'Search failed', { query: q }, error as Error);\n res.status(500).json({ error: 'Search failed' });\n }\n });\n\n // Ricerca ibrida (vettoriale + keyword)\n // Express 5: le Promise rifiutate vengono propagate automaticamente all'error handler;\n // il try/catch interno \u00E8 mantenuto per restituire una risposta HTTP 500 controllata.\n router.get('/api/hybrid-search', async (req, res) => {\n const { q, project, limit } = req.query as { q: string; project?: string; limit?: string };\n\n if (!q) {\n res.status(400).json({ error: 'Query parameter \"q\" is required' });\n return;\n }\n\n try {\n const hybridSearch = getHybridSearch();\n const results = await hybridSearch.search(ctx.db.db, q, {\n project: project || undefined,\n limit: parseIntSafe(limit, 10, 1, 100)\n });\n\n res.json({ results, count: results.length });\n } catch (error) {\n logger.error('WORKER', 'Hybrid search failed', { query: q }, error as Error);\n res.status(500).json({ error: 'Hybrid search failed' });\n }\n });\n\n // Timeline: chronological context around an observation\n router.get('/api/timeline', (req, res) => {\n const { anchor, depth_before, depth_after } = req.query as { anchor: string; depth_before?: string; depth_after?: string };\n\n if (!anchor) {\n res.status(400).json({ error: 'Query parameter \"anchor\" is required' });\n return;\n }\n\n const anchorId = parseIntSafe(anchor, 0, 1, Number.MAX_SAFE_INTEGER);\n if (anchorId === 0) {\n res.status(400).json({ error: 'Invalid \"anchor\" (must be positive integer)' });\n return;\n }\n\n try {\n const timeline = getTimeline(\n ctx.db.db,\n anchorId,\n parseIntSafe(depth_before, 5, 1, 50),\n parseIntSafe(depth_after, 5, 1, 50)\n );\n\n res.json({ timeline });\n } catch (error) {\n logger.error('WORKER', 'Timeline failed', { anchor }, error as Error);\n res.status(500).json({ error: 'Timeline failed' });\n }\n });\n\n return router;\n}\n", "/**\n * Router Analytics: overview, timeline, type distribution, sessions, anomalies.\n */\n\nimport { Router } from 'express';\nimport type { WorkerContext } from '../worker-context.js';\nimport { isValidProject, parseIntSafe } from '../worker-context.js';\nimport { getObservationsTimeline, getTypeDistribution, getSessionStats, getAnalyticsOverview, getHeatmapData } from '../sqlite/Analytics.js';\nimport { AnomalyDetector } from '../analytics/AnomalyDetector.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createAnalyticsRouter(ctx: WorkerContext): Router {\n const router = Router();\n\n router.get('/api/analytics/overview', (req, res) => {\n const { project } = req.query as { project?: string };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const overview = getAnalyticsOverview(ctx.db.db, project || undefined);\n res.json(overview);\n } catch (error) {\n logger.error('WORKER', 'Analytics overview failed', { project }, error as Error);\n res.status(500).json({ error: 'Analytics overview failed' });\n }\n });\n\n router.get('/api/analytics/timeline', (req, res) => {\n const { project, days } = req.query as { project?: string; days?: string };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const timeline = getObservationsTimeline(\n ctx.db.db,\n project || undefined,\n parseIntSafe(days, 30, 1, 365)\n );\n res.json(timeline);\n } catch (error) {\n logger.error('WORKER', 'Analytics timeline failed', { project }, error as Error);\n res.status(500).json({ error: 'Analytics timeline failed' });\n }\n });\n\n router.get('/api/analytics/types', (req, res) => {\n const { project } = req.query as { project?: string };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const distribution = getTypeDistribution(ctx.db.db, project || undefined);\n res.json(distribution);\n } catch (error) {\n logger.error('WORKER', 'Analytics types failed', { project }, error as Error);\n res.status(500).json({ error: 'Analytics types failed' });\n }\n });\n\n router.get('/api/analytics/sessions', (req, res) => {\n const { project } = req.query as { project?: string };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const stats = getSessionStats(ctx.db.db, project || undefined);\n res.json(stats);\n } catch (error) {\n logger.error('WORKER', 'Analytics sessions failed', { project }, error as Error);\n res.status(500).json({ error: 'Analytics sessions failed' });\n }\n });\n\n // Dati giornalieri per la heatmap della timeline interattiva\n router.get('/api/analytics/heatmap', (req, res) => {\n const { project, months } = req.query as { project?: string; months?: string };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const days = getHeatmapData(\n ctx.db.db,\n project || undefined,\n parseIntSafe(months, 6, 1, 24)\n );\n res.json({ days });\n } catch (error) {\n logger.error('WORKER', 'Heatmap data failed', { project }, error as Error);\n res.status(500).json({ error: 'Heatmap data fetch failed' });\n }\n });\n\n // Concepts pi\u00F9 frequenti estratti dal campo observations.concepts (issue #24)\n router.get('/api/concepts', (req, res) => {\n const { project, limit } = req.query as { project?: string; limit?: string };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n const _limit = parseIntSafe(limit, 50, 1, 200);\n\n try {\n // Il campo concepts \u00E8 una stringa CSV \u2014 estrae token, conta occorrenze, ritorna top N\n const sql = project\n ? `SELECT concepts FROM observations WHERE project = ? AND concepts IS NOT NULL AND concepts != ''`\n : `SELECT concepts FROM observations WHERE concepts IS NOT NULL AND concepts != ''`;\n\n const stmt = ctx.db.db.query(sql);\n const rows = project\n ? stmt.all(project) as Array<{ concepts: string }>\n : stmt.all() as Array<{ concepts: string }>;\n\n // Conta le occorrenze di ogni singolo concept\n const counts = new Map<string, number>();\n for (const row of rows) {\n const tokens = row.concepts.split(',').map((t: string) => t.trim()).filter(Boolean);\n for (const token of tokens) {\n counts.set(token, (counts.get(token) ?? 0) + 1);\n }\n }\n\n // Ordina per frequenza decrescente, ritorna i top N\n const result = Array.from(counts.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, _limit)\n .map(([concept, count]) => ({ concept, count }));\n\n res.json(result);\n } catch (error) {\n logger.error('WORKER', 'Concepts fetch failed', { project }, error as Error);\n res.status(500).json({ error: 'Concepts fetch failed' });\n }\n });\n\n // Session anomaly detection using z-score analysis\n router.get('/api/analytics/anomalies', (req, res) => {\n const { project, window: windowParam, threshold: thresholdParam } = req.query as {\n project?: string;\n window?: string;\n threshold?: string;\n };\n\n if (!project) {\n res.status(400).json({ error: 'project parameter is required' });\n return;\n }\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n const windowSize = parseIntSafe(windowParam, 20, 3, 200);\n const threshold = thresholdParam !== undefined\n ? parseFloat(thresholdParam)\n : 2.0;\n\n if (isNaN(threshold) || threshold <= 0 || threshold > 10) {\n res.status(400).json({ error: 'threshold must be a number between 0 and 10' });\n return;\n }\n\n try {\n const detector = new AnomalyDetector(ctx.db.db, windowSize, threshold);\n const anomalies = detector.detectAnomalies(project);\n const baseline = detector.getBaseline(project);\n res.json({ anomalies, baseline, project });\n } catch (error) {\n logger.error('WORKER', 'Anomaly detection failed', { project }, error as Error);\n res.status(500).json({ error: 'Anomaly detection failed' });\n }\n });\n\n return router;\n}\n", "import { Database } from 'bun:sqlite';\n\n/**\n * Analytics module for Kiro Memory.\n * Aggregate queries for metrics dashboard.\n */\n\n// ============================================================================\n// Return types\n// ============================================================================\n\nexport interface TimelineDayEntry {\n day: string;\n count: number;\n}\n\nexport interface TypeDistributionEntry {\n type: string;\n count: number;\n}\n\nexport interface SessionStatsResult {\n total: number;\n completed: number;\n avgDurationMinutes: number;\n}\n\nexport interface TokenEconomicsResult {\n discoveryTokens: number;\n readTokens: number;\n savings: number;\n reductionPct: number;\n}\n\nexport interface AnalyticsOverviewResult {\n observations: number;\n summaries: number;\n sessions: number;\n prompts: number;\n observationsToday: number;\n observationsThisWeek: number;\n staleCount: number;\n knowledgeCount: number;\n tokenEconomics: TokenEconomicsResult;\n}\n\n/** Singola voce giornaliera per la heatmap */\nexport interface HeatmapDayEntry {\n /** Data in formato ISO YYYY-MM-DD */\n date: string;\n /** Numero di osservazioni quel giorno */\n count: number;\n /** Lista progetto che hanno avuto attivit\u00E0 quel giorno */\n projects: string[];\n}\n\n// ============================================================================\n// Query functions\n// ============================================================================\n\n/**\n * Observations per day (last N days).\n * Returns array sorted chronologically (oldest to most recent).\n */\nexport function getObservationsTimeline(\n db: Database,\n project?: string,\n days: number = 30\n): TimelineDayEntry[] {\n const cutoffEpoch = Date.now() - (days * 24 * 60 * 60 * 1000);\n\n const sql = project\n ? `SELECT DATE(datetime(created_at_epoch / 1000, 'unixepoch')) as day, COUNT(*) as count\n FROM observations\n WHERE project = ? AND created_at_epoch >= ?\n GROUP BY day\n ORDER BY day ASC`\n : `SELECT DATE(datetime(created_at_epoch / 1000, 'unixepoch')) as day, COUNT(*) as count\n FROM observations\n WHERE created_at_epoch >= ?\n GROUP BY day\n ORDER BY day ASC`;\n\n const stmt = db.query(sql);\n const rows = project\n ? stmt.all(project, cutoffEpoch) as TimelineDayEntry[]\n : stmt.all(cutoffEpoch) as TimelineDayEntry[];\n\n return rows;\n}\n\n/**\n * Observation distribution by type.\n * Returns array sorted by count descending.\n */\nexport function getTypeDistribution(\n db: Database,\n project?: string\n): TypeDistributionEntry[] {\n const sql = project\n ? `SELECT type, COUNT(*) as count\n FROM observations\n WHERE project = ?\n GROUP BY type\n ORDER BY count DESC`\n : `SELECT type, COUNT(*) as count\n FROM observations\n GROUP BY type\n ORDER BY count DESC`;\n\n const stmt = db.query(sql);\n const rows = project\n ? stmt.all(project) as TypeDistributionEntry[]\n : stmt.all() as TypeDistributionEntry[];\n\n return rows;\n}\n\n/**\n * Session statistics: total, completed, average duration.\n * Single query with conditional aggregation replacing 3 separate queries.\n */\nexport function getSessionStats(\n db: Database,\n project?: string\n): SessionStatsResult {\n const projectFilter = project ? 'WHERE project = ?' : '';\n\n const sql = `\n SELECT\n COUNT(*) as total,\n COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed,\n AVG(CASE\n WHEN status = 'completed' AND completed_at_epoch IS NOT NULL AND completed_at_epoch > started_at_epoch\n THEN (completed_at_epoch - started_at_epoch) / 1000.0 / 60.0\n END) as avg_min\n FROM sessions\n ${projectFilter}\n `;\n\n const row = project\n ? db.query(sql).get(project) as any\n : db.query(sql).get() as any;\n\n return {\n total: row?.total || 0,\n completed: row?.completed || 0,\n avgDurationMinutes: Math.round((row?.avg_min || 0) * 10) / 10,\n };\n}\n\n/**\n * General overview: base counts + daily/weekly trends.\n * Single CTE query replacing the 10 separate queries (fix N+1).\n */\nexport function getAnalyticsOverview(\n db: Database,\n project?: string\n): AnalyticsOverviewResult {\n const now = Date.now();\n const todayStart = now - (now % (24 * 60 * 60 * 1000)); // Start of day UTC\n const weekStart = now - (7 * 24 * 60 * 60 * 1000);\n\n // Single CTE query: 10 counts in a single DB roundtrip\n // Usa $param syntax compatibile sia con bun:sqlite che better-sqlite3\n const projectFilter = project ? 'WHERE project = $project' : '';\n const obsProjectFilter = project ? 'WHERE project = $project' : '';\n\n const sql = `\n WITH\n obs_stats AS (\n SELECT\n COUNT(*) as total,\n COUNT(CASE WHEN created_at_epoch >= $todayStart THEN 1 END) as today,\n COUNT(CASE WHEN created_at_epoch >= $weekStart THEN 1 END) as this_week,\n COUNT(CASE WHEN is_stale = 1 THEN 1 END) as stale,\n COUNT(CASE WHEN type IN ('constraint', 'decision', 'heuristic', 'rejected') THEN 1 END) as knowledge,\n COALESCE(SUM(discovery_tokens), 0) as discovery_tokens,\n COALESCE(SUM(CAST((LENGTH(COALESCE(title, '')) + LENGTH(COALESCE(narrative, ''))) / 4 AS INTEGER)), 0) as read_tokens\n FROM observations\n ${obsProjectFilter}\n ),\n sum_count AS (\n SELECT COUNT(*) as total FROM summaries ${projectFilter}\n ),\n sess_count AS (\n SELECT COUNT(*) as total FROM sessions ${projectFilter}\n ),\n prompt_count AS (\n SELECT COUNT(*) as total FROM prompts ${projectFilter}\n )\n SELECT\n obs_stats.total as observations,\n obs_stats.today as observations_today,\n obs_stats.this_week as observations_this_week,\n obs_stats.stale as stale_count,\n obs_stats.knowledge as knowledge_count,\n obs_stats.discovery_tokens,\n obs_stats.read_tokens,\n sum_count.total as summaries,\n sess_count.total as sessions,\n prompt_count.total as prompts\n FROM obs_stats, sum_count, sess_count, prompt_count\n `;\n\n const params: Record<string, any> = {\n $todayStart: todayStart,\n $weekStart: weekStart\n };\n if (project) {\n params['$project'] = project;\n }\n\n const row = db.query(sql).get(params) as any;\n\n const discoveryTokens = row?.discovery_tokens || 0;\n const readTokens = row?.read_tokens || 0;\n const savings = Math.max(0, discoveryTokens - readTokens);\n const reductionPct = discoveryTokens > 0 ? Math.round((1 - readTokens / discoveryTokens) * 100) : 0;\n\n return {\n observations: row?.observations || 0,\n summaries: row?.summaries || 0,\n sessions: row?.sessions || 0,\n prompts: row?.prompts || 0,\n observationsToday: row?.observations_today || 0,\n observationsThisWeek: row?.observations_this_week || 0,\n staleCount: row?.stale_count || 0,\n knowledgeCount: row?.knowledge_count || 0,\n tokenEconomics: { discoveryTokens, readTokens, savings, reductionPct }\n };\n}\n\n/**\n * Dati giornalieri per la heatmap interattiva della timeline.\n * Restituisce un giorno per riga con conteggio osservazioni e lista progetti distinti.\n * Ordinamento cronologico crescente (dal pi\u00F9 vecchio al pi\u00F9 recente).\n *\n * @param db Istanza del database SQLite\n * @param project Filtro opzionale per progetto\n * @param months Numero di mesi da coprire (default 6, max 24)\n */\nexport function getHeatmapData(\n db: Database,\n project?: string,\n months: number = 6\n): HeatmapDayEntry[] {\n // Calcola la finestra temporale in millisecondi\n const cutoffEpoch = Date.now() - (months * 30 * 24 * 60 * 60 * 1000);\n\n // Query che raggruppa per giorno e aggrega i progetti con GROUP_CONCAT\n const sql = project\n ? `SELECT\n DATE(datetime(created_at_epoch / 1000, 'unixepoch')) as date,\n COUNT(*) as count,\n GROUP_CONCAT(DISTINCT project) as projects_csv\n FROM observations\n WHERE project = ? AND created_at_epoch >= ?\n GROUP BY date\n ORDER BY date ASC`\n : `SELECT\n DATE(datetime(created_at_epoch / 1000, 'unixepoch')) as date,\n COUNT(*) as count,\n GROUP_CONCAT(DISTINCT project) as projects_csv\n FROM observations\n WHERE created_at_epoch >= ?\n GROUP BY date\n ORDER BY date ASC`;\n\n const stmt = db.query(sql);\n const rawRows = project\n ? stmt.all(project, cutoffEpoch) as Array<{ date: string; count: number; projects_csv: string | null }>\n : stmt.all(cutoffEpoch) as Array<{ date: string; count: number; projects_csv: string | null }>;\n\n // Trasforma i risultati: split CSV \u2192 array di progetti\n return rawRows.map(row => ({\n date: row.date,\n count: row.count,\n projects: row.projects_csv ? row.projects_csv.split(',').filter(Boolean) : [],\n }));\n}\n", "import type { Database } from 'bun:sqlite';\nimport { logger } from '../../utils/logger.js';\n\n/**\n * Session anomaly detection using z-score statistical analysis.\n * Identifies sessions that deviate significantly from the project baseline.\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type AnomalyType = 'long-session' | 'high-error-rate' | 'repetitive-commands' | 'no-progress';\n\nexport interface Anomaly {\n /** Numeric primary key of the sessions table row */\n sessionId: number;\n /** UUID-style session identifier from the CLI */\n contentSessionId: string;\n /** Git project name */\n project: string;\n /** Category of anomaly detected */\n type: AnomalyType;\n /** Z-score: how many standard deviations from the mean (0 for heuristic checks) */\n score: number;\n /** Actual measured value for the anomalous metric */\n value: number;\n /** Baseline mean for the metric */\n mean: number;\n /** Baseline standard deviation for the metric */\n stdDev: number;\n /** Human-readable description of the anomaly */\n description: string;\n}\n\nexport interface ProjectBaseline {\n project: string;\n avgDurationMinutes: number;\n stdDurationMinutes: number;\n avgObservations: number;\n stdObservations: number;\n avgCommands: number;\n stdCommands: number;\n /** Number of sessions used to compute the baseline */\n sampleSize: number;\n}\n\n// ============================================================================\n// Row shapes returned by SQLite queries\n// ============================================================================\n\ninterface SessionRow {\n id: number;\n content_session_id: string;\n project: string;\n duration_minutes: number | null;\n obs_count: number;\n cmd_count: number;\n write_count: number;\n}\n\n// ============================================================================\n// AnomalyDetector class\n// ============================================================================\n\n/**\n * Anomaly detector using z-score analysis.\n * Compares each session's metrics against the project baseline\n * computed from the last N completed sessions.\n */\nexport class AnomalyDetector {\n private db: Database;\n private windowSize: number;\n private threshold: number;\n\n /**\n * @param db - bun:sqlite Database instance\n * @param windowSize - Number of recent sessions to compute baseline (default: 20)\n * @param threshold - Z-score threshold for anomaly (default: 2.0 = 2 standard deviations)\n */\n constructor(db: Database, windowSize: number = 20, threshold: number = 2.0) {\n this.db = db;\n this.windowSize = windowSize;\n this.threshold = threshold;\n }\n\n /**\n * Compute baseline statistics for a project from its most recent completed sessions.\n * Returns null when fewer than 3 sessions are available (insufficient sample).\n */\n getBaseline(project: string): ProjectBaseline | null {\n const sessions = this.db.query(`\n SELECT\n s.id,\n s.content_session_id,\n s.started_at_epoch,\n s.completed_at_epoch,\n CASE\n WHEN s.completed_at_epoch IS NOT NULL AND s.completed_at_epoch > s.started_at_epoch\n THEN (s.completed_at_epoch - s.started_at_epoch) / 1000.0 / 60.0\n ELSE NULL\n END as duration_minutes,\n (SELECT COUNT(*) FROM observations o WHERE o.memory_session_id = s.memory_session_id) as obs_count,\n (SELECT COUNT(*) FROM observations o WHERE o.memory_session_id = s.memory_session_id AND o.type = 'command') as cmd_count\n FROM sessions s\n WHERE s.project = ? AND s.status = 'completed' AND s.completed_at_epoch IS NOT NULL\n ORDER BY s.completed_at_epoch DESC, s.id DESC\n LIMIT ?\n `).all(project, this.windowSize) as Array<{\n id: number;\n content_session_id: string;\n duration_minutes: number | null;\n obs_count: number;\n cmd_count: number;\n }>;\n\n // Require at least 3 sessions for a meaningful baseline\n if (sessions.length < 3) {\n logger.debug('DB', `Baseline for \"${project}\": only ${sessions.length} sessions, skipping`);\n return null;\n }\n\n const durations = sessions\n .filter(s => s.duration_minutes !== null)\n .map(s => s.duration_minutes!);\n const obsCounts = sessions.map(s => s.obs_count);\n const cmdCounts = sessions.map(s => s.cmd_count);\n\n return {\n project,\n avgDurationMinutes: mean(durations),\n stdDurationMinutes: stdDev(durations),\n avgObservations: mean(obsCounts),\n stdObservations: stdDev(obsCounts),\n avgCommands: mean(cmdCounts),\n stdCommands: stdDev(cmdCounts),\n sampleSize: sessions.length,\n };\n }\n\n /**\n * Detect anomalous sessions for a project.\n *\n * Checks performed:\n * - long-session : duration z-score > threshold\n * - repetitive-commands : command count z-score > threshold AND cmd/obs ratio > 80 %\n * - no-progress : obs count above average but zero file-writes\n *\n * Returns an empty array when there is not enough baseline data.\n */\n detectAnomalies(project: string): Anomaly[] {\n const baseline = this.getBaseline(project);\n if (!baseline) return [];\n\n const sessions = this.db.query(`\n SELECT\n s.id,\n s.content_session_id,\n s.project,\n s.started_at_epoch,\n s.completed_at_epoch,\n CASE\n WHEN s.completed_at_epoch IS NOT NULL AND s.completed_at_epoch > s.started_at_epoch\n THEN (s.completed_at_epoch - s.started_at_epoch) / 1000.0 / 60.0\n ELSE NULL\n END as duration_minutes,\n (SELECT COUNT(*) FROM observations o WHERE o.memory_session_id = s.memory_session_id) as obs_count,\n (SELECT COUNT(*) FROM observations o WHERE o.memory_session_id = s.memory_session_id AND o.type = 'command') as cmd_count,\n (SELECT COUNT(*) FROM observations o WHERE o.memory_session_id = s.memory_session_id AND o.type = 'file-write') as write_count\n FROM sessions s\n WHERE s.project = ? AND s.status = 'completed' AND s.completed_at_epoch IS NOT NULL\n ORDER BY s.completed_at_epoch DESC, s.id DESC\n LIMIT ?\n `).all(project, this.windowSize) as SessionRow[];\n\n const anomalies: Anomaly[] = [];\n\n for (const session of sessions) {\n // --- Check: long-session ---\n if (session.duration_minutes !== null && baseline.stdDurationMinutes > 0) {\n const zScore = (session.duration_minutes - baseline.avgDurationMinutes) / baseline.stdDurationMinutes;\n if (zScore > this.threshold) {\n anomalies.push({\n sessionId: session.id,\n contentSessionId: session.content_session_id,\n project: session.project,\n type: 'long-session',\n score: Math.round(zScore * 100) / 100,\n value: Math.round(session.duration_minutes * 10) / 10,\n mean: Math.round(baseline.avgDurationMinutes * 10) / 10,\n stdDev: Math.round(baseline.stdDurationMinutes * 10) / 10,\n description: `Session lasted ${Math.round(session.duration_minutes)} minutes (avg: ${Math.round(baseline.avgDurationMinutes)} min)`,\n });\n }\n }\n\n // --- Check: repetitive-commands ---\n // Triggered when both the raw command count z-score AND the cmd/obs ratio exceed thresholds.\n if (session.obs_count > 0 && baseline.stdCommands > 0) {\n const cmdRatio = session.cmd_count / session.obs_count;\n const zScore = (session.cmd_count - baseline.avgCommands) / baseline.stdCommands;\n if (zScore > this.threshold && cmdRatio > 0.8) {\n anomalies.push({\n sessionId: session.id,\n contentSessionId: session.content_session_id,\n project: session.project,\n type: 'repetitive-commands',\n score: Math.round(zScore * 100) / 100,\n value: session.cmd_count,\n mean: Math.round(baseline.avgCommands * 10) / 10,\n stdDev: Math.round(baseline.stdCommands * 10) / 10,\n description: `${session.cmd_count} commands (${Math.round(cmdRatio * 100)}% of observations, avg: ${Math.round(baseline.avgCommands)})`,\n });\n }\n }\n\n // --- Check: no-progress ---\n // Heuristic: observation count above baseline average but no files were written.\n if (session.obs_count > baseline.avgObservations && session.write_count === 0) {\n anomalies.push({\n sessionId: session.id,\n contentSessionId: session.content_session_id,\n project: session.project,\n type: 'no-progress',\n score: 0, // Not z-score based\n value: session.obs_count,\n mean: Math.round(baseline.avgObservations * 10) / 10,\n stdDev: Math.round(baseline.stdObservations * 10) / 10,\n description: `${session.obs_count} observations but no files written`,\n });\n }\n }\n\n return anomalies;\n }\n}\n\n// ============================================================================\n// Statistical helper functions\n// ============================================================================\n\n/** Arithmetic mean of an array of numbers. Returns 0 for empty arrays. */\nexport function mean(values: number[]): number {\n if (values.length === 0) return 0;\n return values.reduce((sum, v) => sum + v, 0) / values.length;\n}\n\n/**\n * Sample standard deviation (Bessel's correction, divides by N-1).\n * Returns 0 for arrays with fewer than 2 elements.\n */\nexport function stdDev(values: number[]): number {\n if (values.length < 2) return 0;\n const avg = mean(values);\n const squaredDiffs = values.map(v => (v - avg) ** 2);\n return Math.sqrt(squaredDiffs.reduce((sum, v) => sum + v, 0) / (values.length - 1));\n}\n", "/**\n * Router Sessions: session list, checkpoints, prompts.\n * L'endpoint /api/prompts supporta keyset pagination tramite parametro `cursor`.\n * Il parametro `offset` \u00E8 mantenuto come fallback deprecato per backward compatibility.\n */\n\nimport { Router } from 'express';\nimport type { WorkerContext } from '../worker-context.js';\nimport { isValidProject, parseIntSafe } from '../worker-context.js';\nimport { getSessionsByProject, getAllSessions } from '../sqlite/Sessions.js';\nimport { getLatestCheckpoint, getLatestCheckpointByProject } from '../sqlite/Checkpoints.js';\nimport { decodeCursor, buildNextCursor } from '../sqlite/cursor.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createSessionsRouter(ctx: WorkerContext): Router {\n const router = Router();\n\n // Session list\n router.get('/api/sessions', (req, res) => {\n const { project } = req.query as { project?: string };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const sessions = project\n ? getSessionsByProject(ctx.db.db, project, 50)\n : getAllSessions(ctx.db.db, 50);\n res.json(sessions);\n } catch (error) {\n logger.error('WORKER', 'Session list failed', { project }, error as Error);\n res.status(500).json({ error: 'Sessions list failed' });\n }\n });\n\n // Checkpoint by session\n router.get('/api/sessions/:id/checkpoint', (req, res) => {\n const sessionId = parseInt(req.params.id, 10);\n\n if (isNaN(sessionId) || sessionId <= 0) {\n res.status(400).json({ error: 'Invalid session ID' });\n return;\n }\n\n try {\n const checkpoint = getLatestCheckpoint(ctx.db.db, sessionId);\n if (!checkpoint) {\n res.status(404).json({ error: 'No checkpoint found for this session' });\n return;\n }\n res.json(checkpoint);\n } catch (error) {\n logger.error('WORKER', 'Checkpoint fetch failed', { sessionId }, error as Error);\n res.status(500).json({ error: 'Checkpoint fetch failed' });\n }\n });\n\n // Checkpoint by project\n router.get('/api/checkpoint', (req, res) => {\n const { project } = req.query as { project?: string };\n\n if (!project) {\n res.status(400).json({ error: 'Project parameter is required' });\n return;\n }\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const checkpoint = getLatestCheckpointByProject(ctx.db.db, project);\n if (!checkpoint) {\n res.status(404).json({ error: 'No checkpoint found for this project' });\n return;\n }\n res.json(checkpoint);\n } catch (error) {\n logger.error('WORKER', 'Project checkpoint fetch failed', { project }, error as Error);\n res.status(500).json({ error: 'Project checkpoint fetch failed' });\n }\n });\n\n // Lista prompt con keyset pagination.\n // Parametri:\n // cursor \u2014 cursor opaco base64 restituito dall'ultima risposta (prima pagina: assente)\n // limit \u2014 elementi per pagina (default 20, max 200)\n // project \u2014 filtro opzionale per progetto\n // offset \u2014 [DEPRECATO] fallback OFFSET-based per compatibilit\u00E0; ignorato se cursor \u00E8 presente\n router.get('/api/prompts', (req, res) => {\n const { cursor, offset, limit, project } = req.query as {\n cursor?: string;\n offset?: string;\n limit?: string;\n project?: string;\n };\n const _limit = parseIntSafe(limit, 20, 1, 200);\n\n try {\n let rows: unknown[];\n\n if (cursor) {\n // Modalit\u00E0 keyset: WHERE (created_at_epoch, id) < (cursorEpoch, cursorId)\n const decoded = decodeCursor(cursor);\n if (!decoded) {\n res.status(400).json({ error: 'Cursor non valido' });\n return;\n }\n\n const sql = project\n ? `SELECT * FROM prompts\n WHERE project = ? AND (created_at_epoch < ? OR (created_at_epoch = ? AND id < ?))\n ORDER BY created_at_epoch DESC, id DESC\n LIMIT ?`\n : `SELECT * FROM prompts\n WHERE (created_at_epoch < ? OR (created_at_epoch = ? AND id < ?))\n ORDER BY created_at_epoch DESC, id DESC\n LIMIT ?`;\n\n rows = project\n ? ctx.db.db.query(sql).all(project, decoded.epoch, decoded.epoch, decoded.id, _limit)\n : ctx.db.db.query(sql).all(decoded.epoch, decoded.epoch, decoded.id, _limit);\n } else if (offset !== undefined) {\n // Modalit\u00E0 fallback OFFSET (deprecata)\n const _offset = parseIntSafe(offset, 0, 0, 1_000_000);\n const sql = project\n ? 'SELECT * FROM prompts WHERE project = ? ORDER BY created_at_epoch DESC, id DESC LIMIT ? OFFSET ?'\n : 'SELECT * FROM prompts ORDER BY created_at_epoch DESC, id DESC LIMIT ? OFFSET ?';\n rows = project\n ? ctx.db.db.query(sql).all(project, _limit, _offset)\n : ctx.db.db.query(sql).all(_limit, _offset);\n } else {\n // Prima pagina senza cursor\n const sql = project\n ? 'SELECT * FROM prompts WHERE project = ? ORDER BY created_at_epoch DESC, id DESC LIMIT ?'\n : 'SELECT * FROM prompts ORDER BY created_at_epoch DESC, id DESC LIMIT ?';\n rows = project\n ? ctx.db.db.query(sql).all(project, _limit)\n : ctx.db.db.query(sql).all(_limit);\n }\n\n // Costruisce il cursor per la pagina successiva\n const typedRows = rows as Array<{ id: number; created_at_epoch: number }>;\n const next_cursor = buildNextCursor(typedRows, _limit);\n\n // Header X-Total-Count per backward compatibility (solo in modalit\u00E0 non-cursor)\n if (!cursor) {\n const countSql = project\n ? 'SELECT COUNT(*) as total FROM prompts WHERE project = ?'\n : 'SELECT COUNT(*) as total FROM prompts';\n const { total } = (project\n ? ctx.db.db.query(countSql).get(project)\n : ctx.db.db.query(countSql).get()) as { total: number };\n res.setHeader('X-Total-Count', total);\n }\n\n res.json({ data: rows, next_cursor, has_more: next_cursor !== null });\n } catch (error) {\n logger.error('WORKER', 'Prompt list failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to list prompts' });\n }\n });\n\n return router;\n}\n", "/**\n * Router Projects: project list, aliases, project statistics.\n */\n\nimport { Router } from 'express';\nimport type { WorkerContext } from '../worker-context.js';\nimport { isValidProject } from '../worker-context.js';\nimport { projectsCache, PROJECTS_CACHE_TTL } from '../worker-context.js';\nimport { getProjectStats } from '../sqlite/Search.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createProjectsRouter(ctx: WorkerContext): Router {\n const router = Router();\n\n // Distinct project list (with 60s cache TTL)\n router.get('/api/projects', (_req, res) => {\n try {\n const now = Date.now();\n if (now - projectsCache.ts < PROJECTS_CACHE_TTL && projectsCache.data.length > 0) {\n res.json(projectsCache.data);\n return;\n }\n\n const stmt = ctx.db.db.query(\n `SELECT DISTINCT project FROM (\n SELECT project FROM observations\n UNION\n SELECT project FROM summaries\n UNION\n SELECT project FROM prompts\n ) ORDER BY project ASC`\n );\n const rows = stmt.all() as { project: string }[];\n projectsCache.data = rows.map(r => r.project);\n projectsCache.ts = now;\n res.json(projectsCache.data);\n } catch (error) {\n logger.error('WORKER', 'Project list failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to list projects' });\n }\n });\n\n // GET project aliases\n router.get('/api/project-aliases', (_req, res) => {\n try {\n const stmt = ctx.db.db.query('SELECT project_name, display_name FROM project_aliases');\n const rows = stmt.all() as { project_name: string; display_name: string }[];\n const aliases: Record<string, string> = {};\n for (const row of rows) {\n aliases[row.project_name] = row.display_name;\n }\n res.json(aliases);\n } catch (error) {\n logger.error('WORKER', 'Alias list failed', {}, error as Error);\n res.status(500).json({ error: 'Failed to list project aliases' });\n }\n });\n\n // PUT project alias (create or update)\n router.put('/api/project-aliases/:project', (req, res) => {\n const { project } = req.params;\n const { displayName } = req.body;\n\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n if (!displayName || typeof displayName !== 'string' || displayName.trim().length === 0 || displayName.length > 100) {\n res.status(400).json({ error: 'Field \"displayName\" is required (string, max 100 chars)' });\n return;\n }\n\n try {\n const now = new Date().toISOString();\n const stmt = ctx.db.db.query(`\n INSERT INTO project_aliases (project_name, display_name, created_at, updated_at)\n VALUES (?, ?, ?, ?)\n ON CONFLICT(project_name) DO UPDATE SET display_name = excluded.display_name, updated_at = excluded.updated_at\n `);\n stmt.run(project, displayName.trim(), now, now);\n res.json({ ok: true, project_name: project, display_name: displayName.trim() });\n } catch (error) {\n logger.error('WORKER', 'Alias update failed', { project }, error as Error);\n res.status(500).json({ error: 'Failed to update project alias' });\n }\n });\n\n // Project statistics\n router.get('/api/stats/:project', (req, res) => {\n const { project } = req.params;\n\n if (!isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n try {\n const stats = getProjectStats(ctx.db.db, project);\n res.json(stats);\n } catch (error) {\n logger.error('WORKER', 'Stats failed', { project }, error as Error);\n res.status(500).json({ error: 'Stats failed' });\n }\n });\n\n return router;\n}\n", "/**\n * Router Data: embeddings, retention, export, report.\n * Handles heavy data operations and maintenance.\n */\n\nimport { Router } from 'express';\nimport type { Request, Response, NextFunction } from 'express';\nimport type { WorkerContext } from '../worker-context.js';\nimport { isValidProject, parseIntSafe } from '../worker-context.js';\nimport { getEmbeddingService } from '../search/EmbeddingService.js';\nimport { getVectorSearch } from '../search/VectorSearch.js';\nimport { getReportData } from '../sqlite/Reports.js';\nimport { formatReportMarkdown } from '../report-formatter.js';\nimport { getRetentionStats, applyRetention, buildRetentionConfig } from '../sqlite/Retention.js';\nimport { listConfig } from '../../cli/cli-utils.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createDataRouter(ctx: WorkerContext, workerToken?: string): Router {\n const router = Router();\n\n /** Middleware: richiede X-Worker-Token per gli endpoint distruttivi */\n function requireAuth(req: Request, res: Response, next: NextFunction): void {\n if (!workerToken) { next(); return; } // No token configured, skip\n const token = req.headers['x-worker-token'] as string;\n if (token !== workerToken) {\n res.status(401).json({ error: 'Invalid or missing X-Worker-Token' });\n return;\n }\n next();\n }\n\n // \u2500\u2500 Embeddings \u2500\u2500\n\n // Backfill embedding per le osservazioni senza vettore\n // Express 5: le Promise rifiutate vengono propagate automaticamente all'error handler;\n // il try/catch interno \u00E8 mantenuto per restituire una risposta HTTP 500 controllata.\n router.post('/api/embeddings/backfill', requireAuth, async (req, res) => {\n const { batchSize } = req.body || {};\n\n try {\n const vectorSearch = getVectorSearch();\n const count = await vectorSearch.backfillEmbeddings(\n ctx.db.db,\n parseIntSafe(String(batchSize), 50, 1, 500)\n );\n res.json({ success: true, generated: count });\n } catch (error) {\n logger.error('WORKER', 'Backfill embeddings failed', {}, error as Error);\n res.status(500).json({ error: 'Backfill failed' });\n }\n });\n\n // Embedding statistics\n router.get('/api/embeddings/stats', (_req, res) => {\n try {\n const vectorSearch = getVectorSearch();\n const stats = vectorSearch.getStats(ctx.db.db);\n const embeddingService = getEmbeddingService();\n\n res.json({\n ...stats,\n provider: embeddingService.getProvider(),\n dimensions: embeddingService.getDimensions(),\n available: embeddingService.isAvailable()\n });\n } catch (error) {\n logger.error('WORKER', 'Embedding stats failed', {}, error as Error);\n res.status(500).json({ error: 'Stats failed' });\n }\n });\n\n // \u2500\u2500 Retention Policy \u2500\u2500\n\n /**\n * GET /api/retention/preview\n * Dry-run: restituisce il conteggio dei record che verrebbero eliminati\n * secondo la configurazione retention corrente, senza modificare il DB.\n */\n router.get('/api/retention/preview', (_req, res) => {\n try {\n const config = buildRetentionConfig(listConfig());\n const stats = getRetentionStats(ctx.db.db, config);\n res.json({ dryRun: true, config, wouldDelete: stats });\n } catch (error) {\n logger.error('WORKER', 'Retention preview failed', {}, error as Error);\n res.status(500).json({ error: 'Retention preview failed' });\n }\n });\n\n /**\n * POST /api/retention/apply\n * Esegue il cleanup retention secondo la configurazione corrente.\n * Richiede X-Worker-Token per proteggere l'operazione distruttiva.\n */\n router.post('/api/retention/apply', requireAuth, (_req, res) => {\n try {\n const config = buildRetentionConfig(listConfig());\n const result = applyRetention(ctx.db.db, config);\n ctx.invalidateProjectsCache();\n\n logger.info('WORKER', `Retention apply: ${result.observations} obs, ${result.summaries} sum, ${result.prompts} prompts, ${result.knowledge} knowledge eliminati`);\n res.json({ success: true, config, deleted: result });\n } catch (error) {\n logger.error('WORKER', 'Retention apply failed', {}, error as Error);\n res.status(500).json({ error: 'Retention apply failed' });\n }\n });\n\n /**\n * POST /api/retention/cleanup\n * Endpoint legacy mantenuto per backward compatibility.\n * Usa la configurazione inline del body (maxAgeDays, dryRun).\n * @deprecated Usare /api/retention/preview e /api/retention/apply\n */\n router.post('/api/retention/cleanup', requireAuth, (req, res) => {\n const { maxAgeDays, dryRun } = req.body || {};\n const days = parseIntSafe(String(maxAgeDays), 90, 7, 730);\n const threshold = Date.now() - (days * 86_400_000);\n\n try {\n if (dryRun) {\n const obsCount = (ctx.db.db.query('SELECT COUNT(*) as c FROM observations WHERE created_at_epoch < ?').get(threshold) as { c: number }).c;\n const sumCount = (ctx.db.db.query('SELECT COUNT(*) as c FROM summaries WHERE created_at_epoch < ?').get(threshold) as { c: number }).c;\n const promptCount = (ctx.db.db.query('SELECT COUNT(*) as c FROM prompts WHERE created_at_epoch < ?').get(threshold) as { c: number }).c;\n res.json({ dryRun: true, maxAgeDays: days, wouldDelete: { observations: obsCount, summaries: sumCount, prompts: promptCount } });\n return;\n }\n\n const cleanup = ctx.db.db.transaction(() => {\n ctx.db.db.run('DELETE FROM observation_embeddings WHERE observation_id IN (SELECT id FROM observations WHERE created_at_epoch < ?)', [threshold]);\n const obsResult = ctx.db.db.run('DELETE FROM observations WHERE created_at_epoch < ?', [threshold]);\n const sumResult = ctx.db.db.run('DELETE FROM summaries WHERE created_at_epoch < ?', [threshold]);\n const promptResult = ctx.db.db.run('DELETE FROM prompts WHERE created_at_epoch < ?', [threshold]);\n return {\n observations: obsResult.changes,\n summaries: sumResult.changes,\n prompts: promptResult.changes,\n };\n });\n\n const deleted = cleanup();\n ctx.invalidateProjectsCache();\n\n logger.info('WORKER', `Retention cleanup (legacy): deleted ${deleted.observations} obs, ${deleted.summaries} sum, ${deleted.prompts} prompts (> ${days}d)`);\n res.json({ success: true, maxAgeDays: days, deleted });\n } catch (error) {\n logger.error('WORKER', 'Retention cleanup failed', { maxAgeDays: days }, error as Error);\n res.status(500).json({ error: 'Retention cleanup failed' });\n }\n });\n\n // \u2500\u2500 Export \u2500\u2500\n\n router.get('/api/export', (req, res) => {\n const { project, format: fmt, type, days } = req.query as {\n project?: string; format?: string; type?: string; days?: string;\n };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n const daysBack = parseIntSafe(days, 30, 1, 365);\n const threshold = Date.now() - (daysBack * 86_400_000);\n\n try {\n let sql = 'SELECT * FROM observations WHERE created_at_epoch > ?';\n const params: (string | number)[] = [threshold];\n if (project) { sql += ' AND project = ?'; params.push(project); }\n if (type) { sql += ' AND type = ?'; params.push(type); }\n sql += ' ORDER BY created_at_epoch DESC LIMIT 1000';\n const observations = ctx.db.db.query(sql).all(...params) as any[];\n\n let sumSql = 'SELECT * FROM summaries WHERE created_at_epoch > ?';\n const sumParams: (string | number)[] = [threshold];\n if (project) { sumSql += ' AND project = ?'; sumParams.push(project); }\n sumSql += ' ORDER BY created_at_epoch DESC LIMIT 100';\n const summaries = ctx.db.db.query(sumSql).all(...sumParams) as any[];\n\n if (fmt === 'markdown' || fmt === 'md') {\n const lines: string[] = [\n `# Kiro Memory Export`,\n `> Project: ${project || 'All'} | Period: ${daysBack} days | Generated: ${new Date().toISOString()}`,\n '',\n `## Observations (${observations.length})`,\n '',\n ];\n\n for (const obs of observations) {\n const date = new Date(obs.created_at_epoch).toISOString().split('T')[0];\n lines.push(`### [${obs.type}] ${obs.title}`);\n lines.push(`- **Date**: ${date} | **Project**: ${obs.project} | **ID**: #${obs.id}`);\n if (obs.narrative) lines.push(`- ${obs.narrative}`);\n if (obs.concepts) lines.push(`- **Concepts**: ${obs.concepts}`);\n lines.push('');\n }\n\n lines.push(`## Summaries (${summaries.length})`, '');\n for (const sum of summaries) {\n const date = new Date(sum.created_at_epoch).toISOString().split('T')[0];\n lines.push(`### Session ${sum.session_id} (${date})`);\n if (sum.request) lines.push(`- **Request**: ${sum.request}`);\n if (sum.completed) lines.push(`- **Completed**: ${sum.completed}`);\n if (sum.next_steps) lines.push(`- **Next steps**: ${sum.next_steps}`);\n lines.push('');\n }\n\n res.type('text/markdown').send(lines.join('\\n'));\n } else {\n res.json({\n meta: { project: project || 'all', daysBack, exportedAt: new Date().toISOString() },\n observations,\n summaries,\n });\n }\n } catch (error) {\n logger.error('WORKER', 'Export failed', { project, fmt }, error as Error);\n res.status(500).json({ error: 'Export failed' });\n }\n });\n\n // \u2500\u2500 Report \u2500\u2500\n\n router.get('/api/report', (req, res) => {\n const { project, period, format } = req.query as {\n project?: string; period?: string; format?: string;\n };\n\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Invalid project name' });\n return;\n }\n\n const validPeriods = ['weekly', 'monthly'];\n const reportPeriod = validPeriods.includes(period || '') ? period : 'weekly';\n const daysBack = reportPeriod === 'monthly' ? 30 : 7;\n const now = Date.now();\n const startEpoch = now - (daysBack * 24 * 60 * 60 * 1000);\n\n try {\n const data = getReportData(ctx.db.db, project || undefined, startEpoch, now);\n\n const outputFormat = format || 'json';\n if (outputFormat === 'markdown' || outputFormat === 'md') {\n res.type('text/markdown').send(formatReportMarkdown(data));\n } else if (outputFormat === 'text') {\n res.type('text/plain').send(JSON.stringify(data, null, 2));\n } else {\n res.json(data);\n }\n } catch (error) {\n logger.error('WORKER', 'Report generation failed', { project, period }, error as Error);\n res.status(500).json({ error: 'Report generation failed' });\n }\n });\n\n return router;\n}\n", "import type { ReportData } from '../types/worker-types.js';\n\n/**\n * Report formatters for Kiro Memory.\n * Three outputs: ANSI text (CLI), markdown (file/sharing), JSON (automations).\n */\n\n// ============================================================================\n// ANSI text format (for CLI)\n// ============================================================================\n\nexport function formatReportText(data: ReportData): string {\n const lines: string[] = [];\n\n // Header\n lines.push('');\n lines.push(` \\x1b[36m\u2550\u2550\u2550 Kiro Memory Report \u2014 ${data.period.label} \u2550\u2550\u2550\\x1b[0m`);\n lines.push(` \\x1b[2m${data.period.start} \u2192 ${data.period.end} (${data.period.days} days)\\x1b[0m`);\n lines.push('');\n\n // Overview\n lines.push(` \\x1b[1mOverview\\x1b[0m`);\n lines.push(` Observations: ${data.overview.observations}`);\n lines.push(` Summaries: ${data.overview.summaries}`);\n lines.push(` Sessions: ${data.overview.sessions}`);\n lines.push(` Prompts: ${data.overview.prompts}`);\n lines.push(` Knowledge: ${data.overview.knowledgeCount}`);\n if (data.overview.staleCount > 0) {\n lines.push(` Stale: ${data.overview.staleCount}`);\n }\n lines.push('');\n\n // Session stats\n if (data.sessionStats.total > 0) {\n const completionPct = data.sessionStats.total > 0\n ? Math.round((data.sessionStats.completed / data.sessionStats.total) * 100)\n : 0;\n lines.push(` \\x1b[1mSessions\\x1b[0m`);\n lines.push(` Total: ${data.sessionStats.total} | Completed: ${data.sessionStats.completed} (${completionPct}%)`);\n if (data.sessionStats.avgDurationMinutes > 0) {\n lines.push(` Avg duration: ${data.sessionStats.avgDurationMinutes} min`);\n }\n lines.push('');\n }\n\n // Timeline (bar chart ASCII)\n if (data.timeline.length > 0) {\n lines.push(` \\x1b[1mTimeline\\x1b[0m`);\n const maxCount = Math.max(...data.timeline.map(t => t.count));\n const maxBarLen = 30;\n for (const entry of data.timeline) {\n const barLen = maxCount > 0 ? Math.round((entry.count / maxCount) * maxBarLen) : 0;\n const bar = '\\x1b[32m' + '\u2593'.repeat(barLen) + '\\x1b[0m';\n const dayShort = entry.day.substring(5); // MM-DD\n lines.push(` ${dayShort} ${bar} ${entry.count}`);\n }\n lines.push('');\n }\n\n // Type distribution\n if (data.typeDistribution.length > 0) {\n lines.push(` \\x1b[1mBy Type\\x1b[0m`);\n for (const entry of data.typeDistribution) {\n lines.push(` ${entry.type.padEnd(16)} ${entry.count}`);\n }\n lines.push('');\n }\n\n // Learnings\n if (data.topLearnings.length > 0) {\n lines.push(` \\x1b[1mKey Learnings\\x1b[0m`);\n for (const learning of data.topLearnings) {\n lines.push(` - ${learning}`);\n }\n lines.push('');\n }\n\n // Completed tasks\n if (data.completedTasks.length > 0) {\n lines.push(` \\x1b[1mCompleted\\x1b[0m`);\n for (const task of data.completedTasks) {\n lines.push(` - ${task}`);\n }\n lines.push('');\n }\n\n // Next steps\n if (data.nextSteps.length > 0) {\n lines.push(` \\x1b[1mNext Steps\\x1b[0m`);\n for (const step of data.nextSteps) {\n lines.push(` - ${step}`);\n }\n lines.push('');\n }\n\n // File hotspots\n if (data.fileHotspots.length > 0) {\n lines.push(` \\x1b[1mFile Hotspots\\x1b[0m`);\n for (const entry of data.fileHotspots.slice(0, 10)) {\n lines.push(` ${entry.file} (${entry.count}x)`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\n// ============================================================================\n// Markdown format (for file/sharing)\n// ============================================================================\n\nexport function formatReportMarkdown(data: ReportData): string {\n const lines: string[] = [];\n\n // Header\n lines.push(`# Kiro Memory Report \u2014 ${data.period.label}`);\n lines.push('');\n lines.push(`**Period**: ${data.period.start} \u2192 ${data.period.end} (${data.period.days} days)`);\n lines.push('');\n\n // Overview table\n lines.push('## Overview');\n lines.push('');\n lines.push('| Metric | Count |');\n lines.push('|--------|------:|');\n lines.push(`| Observations | ${data.overview.observations} |`);\n lines.push(`| Summaries | ${data.overview.summaries} |`);\n lines.push(`| Sessions | ${data.overview.sessions} |`);\n lines.push(`| Prompts | ${data.overview.prompts} |`);\n lines.push(`| Knowledge items | ${data.overview.knowledgeCount} |`);\n if (data.overview.staleCount > 0) {\n lines.push(`| Stale observations | ${data.overview.staleCount} |`);\n }\n lines.push('');\n\n // Sessions\n if (data.sessionStats.total > 0) {\n const completionPct = Math.round((data.sessionStats.completed / data.sessionStats.total) * 100);\n lines.push('## Sessions');\n lines.push('');\n lines.push(`- **Total**: ${data.sessionStats.total}`);\n lines.push(`- **Completed**: ${data.sessionStats.completed} (${completionPct}%)`);\n if (data.sessionStats.avgDurationMinutes > 0) {\n lines.push(`- **Avg duration**: ${data.sessionStats.avgDurationMinutes} min`);\n }\n lines.push('');\n }\n\n // Timeline\n if (data.timeline.length > 0) {\n lines.push('## Activity Timeline');\n lines.push('');\n lines.push('| Date | Observations |');\n lines.push('|------|------------:|');\n for (const entry of data.timeline) {\n lines.push(`| ${entry.day} | ${entry.count} |`);\n }\n lines.push('');\n }\n\n // Type distribution\n if (data.typeDistribution.length > 0) {\n lines.push('## Observation Types');\n lines.push('');\n for (const entry of data.typeDistribution) {\n lines.push(`- **${entry.type}**: ${entry.count}`);\n }\n lines.push('');\n }\n\n // Learnings\n if (data.topLearnings.length > 0) {\n lines.push('## Key Learnings');\n lines.push('');\n for (const learning of data.topLearnings) {\n lines.push(`- ${learning}`);\n }\n lines.push('');\n }\n\n // Completed\n if (data.completedTasks.length > 0) {\n lines.push('## Completed');\n lines.push('');\n for (const task of data.completedTasks) {\n lines.push(`- ${task}`);\n }\n lines.push('');\n }\n\n // Next steps\n if (data.nextSteps.length > 0) {\n lines.push('## Next Steps');\n lines.push('');\n for (const step of data.nextSteps) {\n lines.push(`- ${step}`);\n }\n lines.push('');\n }\n\n // File hotspots\n if (data.fileHotspots.length > 0) {\n lines.push('## File Hotspots');\n lines.push('');\n lines.push('| File | Modifications |');\n lines.push('|------|-------------:|');\n for (const entry of data.fileHotspots.slice(0, 10)) {\n lines.push(`| \\`${entry.file}\\` | ${entry.count} |`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\n// ============================================================================\n// JSON format (for automations/webhooks)\n// ============================================================================\n\nexport function formatReportJson(data: ReportData): string {\n return JSON.stringify(data, null, 2);\n}\n", "/**\n * Router Webhooks GitHub: riceve eventi, valida la firma HMAC-SHA256,\n * persiste i link nella tabella github_links, espone API di query.\n *\n * Endpoints:\n * POST /api/webhooks/github \u2014 ricevitore eventi GitHub\n * GET /api/github/links \u2014 query links (filtri: repo, issue, pr, observation_id, limit)\n * GET /api/github/repos \u2014 lista repo con conteggio link\n */\n\nimport { Router } from 'express';\nimport type { Request, Response, NextFunction } from 'express';\nimport crypto from 'crypto';\nimport rateLimit from 'express-rate-limit';\nimport type { WorkerContext } from '../worker-context.js';\nimport { parseIntSafe } from '../worker-context.js';\nimport {\n createGithubLink,\n getGithubLinksByObservation,\n getGithubLinksByRepo,\n getGithubLinksByIssue,\n getGithubLinksByPR,\n searchGithubLinks,\n listReposWithLinkCount,\n} from '../sqlite/GithubLinks.js';\nimport { logger } from '../../utils/logger.js';\n\n// \u2500\u2500 Costanti \u2500\u2500\n\n// Regex per rilevare riferimenti a issue/PR nei messaggi di commit (#NNN)\nconst ISSUE_REF_REGEX = /#(\\d+)/g;\n\n// Tipi di evento supportati\nconst SUPPORTED_EVENTS = new Set(['issues', 'pull_request', 'push']);\n\n// Azioni supportate per issues\nconst ISSUE_ACTIONS = new Set(['opened', 'closed', 'reopened', 'labeled']);\n\n// Azioni supportate per pull_request\nconst PR_ACTIONS = new Set(['opened', 'closed', 'reopened', 'review_requested', 'merged']);\n\n// \u2500\u2500 Helpers \u2500\u2500\n\n/**\n * Recupera il segreto webhook dalla configurazione o dalla variabile d'ambiente.\n * Restituisce null se non configurato (webhook non autenticati saranno rifiutati).\n */\nfunction getWebhookSecret(ctx: WorkerContext): string | null {\n // Prova prima dal context config, poi dalla variabile d'ambiente\n try {\n const fromConfig = (ctx as any).getConfig?.('github.webhook_secret');\n if (fromConfig && typeof fromConfig === 'string' && fromConfig.trim().length > 0) {\n return fromConfig.trim();\n }\n } catch {\n // getConfig non disponibile in tutti i contesti, ignora\n }\n return process.env.KIRO_MEMORY_GITHUB_WEBHOOK_SECRET || null;\n}\n\n/**\n * Valida la firma HMAC-SHA256 del payload GitHub.\n * GitHub invia l'header X-Hub-Signature-256: sha256=<hex>\n * Usa confronto timing-safe per prevenire timing attacks.\n */\nexport function validateGithubSignature(\n payload: Buffer,\n signature: string | undefined,\n secret: string\n): boolean {\n if (!signature || !signature.startsWith('sha256=')) {\n return false;\n }\n\n const expectedSignature = 'sha256=' + crypto\n .createHmac('sha256', secret)\n .update(payload)\n .digest('hex');\n\n // Confronto timing-safe (evita timing side-channel)\n try {\n return crypto.timingSafeEqual(\n Buffer.from(signature, 'utf-8'),\n Buffer.from(expectedSignature, 'utf-8')\n );\n } catch {\n // Le stringhe hanno lunghezze diverse \u2014 firma non valida\n return false;\n }\n}\n\n/**\n * Estrae i riferimenti a issue/PR (#NNN) da una stringa di testo.\n * Restituisce un array di numeri interi unici.\n */\nexport function extractIssueRefs(text: string): number[] {\n const refs = new Set<number>();\n const regex = /#(\\d+)/g;\n let match: RegExpExecArray | null;\n while ((match = regex.exec(text)) !== null) {\n const num = parseInt(match[1], 10);\n if (num > 0 && num < 1_000_000) {\n refs.add(num);\n }\n }\n return Array.from(refs);\n}\n\n// \u2500\u2500 Processori eventi (asincroni, fire-and-forget) \u2500\u2500\n\n/**\n * Processa un evento issues di GitHub.\n * Crea un github_link per ogni azione supported.\n */\nfunction processIssuesEvent(ctx: WorkerContext, payload: any, repo: string): void {\n const { action, issue } = payload;\n if (!ISSUE_ACTIONS.has(action) || !issue) return;\n\n try {\n createGithubLink(ctx.db.db, {\n repo,\n issue_number: issue.number,\n event_type: 'issues',\n action,\n title: issue.title || null,\n url: issue.html_url || null,\n author: issue.user?.login || null,\n });\n logger.debug('WEBHOOK', `Issue #${issue.number} (${action}) salvato per ${repo}`);\n } catch (err) {\n logger.error('WEBHOOK', `Errore salvataggio issue event per ${repo}`, {}, err as Error);\n }\n}\n\n/**\n * Processa un evento pull_request di GitHub.\n * Gestisce lo stato merged distinguendolo da closed.\n */\nfunction processPullRequestEvent(ctx: WorkerContext, payload: any, repo: string): void {\n const { action, pull_request: pr } = payload;\n if (!pr) return;\n\n // GitHub segnala il merge come action=closed con merged=true\n const effectiveAction = action === 'closed' && pr.merged ? 'merged' : action;\n if (!PR_ACTIONS.has(effectiveAction)) return;\n\n try {\n createGithubLink(ctx.db.db, {\n repo,\n pr_number: pr.number,\n event_type: 'pull_request',\n action: effectiveAction,\n title: pr.title || null,\n url: pr.html_url || null,\n author: pr.user?.login || null,\n });\n logger.debug('WEBHOOK', `PR #${pr.number} (${effectiveAction}) salvato per ${repo}`);\n } catch (err) {\n logger.error('WEBHOOK', `Errore salvataggio PR event per ${repo}`, {}, err as Error);\n }\n}\n\n/**\n * Processa un evento push di GitHub.\n * Scansiona i messaggi di commit alla ricerca di riferimenti #NNN.\n * Per ogni riferimento trovato crea un github_link.\n */\nfunction processPushEvent(ctx: WorkerContext, payload: any, repo: string): void {\n const { commits, pusher, ref } = payload;\n if (!Array.isArray(commits) || commits.length === 0) return;\n\n const branch = typeof ref === 'string' ? ref.replace('refs/heads/', '') : null;\n const author = pusher?.name || null;\n\n for (const commit of commits) {\n const message: string = commit.message || '';\n const commitUrl: string = commit.url || null;\n const issueRefs = extractIssueRefs(message);\n\n for (const issueNumber of issueRefs) {\n try {\n createGithubLink(ctx.db.db, {\n repo,\n issue_number: issueNumber,\n event_type: 'push',\n action: branch ? `push:${branch}` : 'push',\n title: message.split('\\n')[0].substring(0, 500) || null,\n url: commitUrl,\n author,\n });\n logger.debug('WEBHOOK', `Riferimento issue #${issueNumber} trovato nel commit di ${repo}`);\n } catch (err) {\n logger.error('WEBHOOK', `Errore salvataggio push ref per ${repo}`, {}, err as Error);\n }\n }\n }\n}\n\n// \u2500\u2500 Middleware raw body \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Legge il body come Buffer prima che express.json() lo consumi,\n// necessario per la validazione HMAC della firma GitHub.\n\nfunction rawBodyMiddleware(req: Request, _res: Response, next: NextFunction): void {\n const chunks: Buffer[] = [];\n\n req.on('data', (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n req.on('end', () => {\n const rawBody = Buffer.concat(chunks);\n (req as any).rawBody = rawBody;\n\n // Parsa il JSON manualmente se il Content-Type lo indica\n const contentType = req.headers['content-type'] || '';\n if (contentType.includes('application/json') && rawBody.length > 0) {\n try {\n req.body = JSON.parse(rawBody.toString('utf-8'));\n } catch {\n req.body = {};\n }\n }\n\n next();\n });\n\n req.on('error', () => {\n (req as any).rawBody = Buffer.alloc(0);\n next();\n });\n}\n\n// \u2500\u2500 Router factory \u2500\u2500\n\nexport function createWebhooksRouter(ctx: WorkerContext): Router {\n const router = Router();\n\n // Rate limit dedicato per il webhook: 30 req/min\n const webhookLimiter = rateLimit({\n windowMs: 60_000,\n max: 30,\n standardHeaders: true,\n legacyHeaders: false,\n message: { error: 'Troppo molte richieste al webhook, riprova tra poco' }\n });\n\n // \u2500\u2500 POST /api/webhooks/github \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Ricevitore principale degli eventi GitHub webhook.\n // Risponde 200 immediatamente, processa il payload in background.\n router.post(\n '/api/webhooks/github',\n webhookLimiter,\n rawBodyMiddleware,\n (req: Request, res: Response) => {\n const secret = getWebhookSecret(ctx);\n\n // Se il segreto \u00E8 configurato, valida la firma\n if (secret) {\n const signature = req.headers['x-hub-signature-256'] as string | undefined;\n const rawBody: Buffer = (req as any).rawBody;\n\n if (!rawBody) {\n res.status(400).json({ error: 'Payload non leggibile' });\n return;\n }\n\n if (!validateGithubSignature(rawBody, signature, secret)) {\n logger.warn('WEBHOOK', 'Firma GitHub non valida', {\n hasSignature: !!signature,\n });\n res.status(401).json({ error: 'Firma non valida o mancante' });\n return;\n }\n }\n\n const eventType = req.headers['x-github-event'] as string | undefined;\n\n if (!eventType || !SUPPORTED_EVENTS.has(eventType)) {\n // Accetta silenziosamente gli eventi non gestiti (GitHub li invia comunque)\n res.json({ ok: true, processed: false, reason: 'Evento non gestito' });\n return;\n }\n\n // Risponde subito con 200 \u2014 GitHub si aspetta una risposta rapida\n res.json({ ok: true, processed: true });\n\n // Elaborazione asincrona del payload (fire-and-forget)\n setImmediate(() => {\n try {\n const payload = req.body;\n if (!payload || typeof payload !== 'object') return;\n\n // Estrae il nome del repository dal payload\n const repo: string = payload.repository?.full_name\n || payload.repository?.name\n || 'unknown';\n\n switch (eventType) {\n case 'issues':\n processIssuesEvent(ctx, payload, repo);\n break;\n case 'pull_request':\n processPullRequestEvent(ctx, payload, repo);\n break;\n case 'push':\n processPushEvent(ctx, payload, repo);\n break;\n }\n } catch (err) {\n logger.error('WEBHOOK', 'Errore elaborazione evento webhook', {}, err as Error);\n }\n });\n }\n );\n\n // \u2500\u2500 GET /api/github/links \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Query sui link GitHub con filtri opzionali.\n // Parametri: repo, issue, pr, observation_id, query, limit (default 20, max 200)\n router.get('/api/github/links', (req: Request, res: Response) => {\n const { repo, issue, pr, observation_id, query } = req.query as {\n repo?: string;\n issue?: string;\n pr?: string;\n observation_id?: string;\n query?: string;\n };\n const limit = parseIntSafe(req.query.limit as string | undefined, 20, 1, 200);\n\n try {\n let links;\n\n if (observation_id) {\n // Filtro per observation\n const obsId = parseInt(observation_id, 10);\n if (isNaN(obsId) || obsId <= 0) {\n res.status(400).json({ error: '\"observation_id\" deve essere un intero positivo' });\n return;\n }\n links = getGithubLinksByObservation(ctx.db.db, obsId);\n } else if (repo && issue) {\n // Filtro per repo + issue\n const issueNum = parseInt(issue, 10);\n if (isNaN(issueNum) || issueNum <= 0) {\n res.status(400).json({ error: '\"issue\" deve essere un intero positivo' });\n return;\n }\n links = getGithubLinksByIssue(ctx.db.db, repo, issueNum);\n } else if (repo && pr) {\n // Filtro per repo + PR\n const prNum = parseInt(pr, 10);\n if (isNaN(prNum) || prNum <= 0) {\n res.status(400).json({ error: '\"pr\" deve essere un intero positivo' });\n return;\n }\n links = getGithubLinksByPR(ctx.db.db, repo, prNum);\n } else if (repo) {\n // Filtro per repo con limit\n links = getGithubLinksByRepo(ctx.db.db, repo, limit);\n } else {\n // Ricerca testuale generica\n links = searchGithubLinks(ctx.db.db, query || '', { limit });\n }\n\n res.json({ links, total: links.length });\n } catch (err) {\n logger.error('WORKER', 'Errore query github links', {}, err as Error);\n res.status(500).json({ error: 'Errore nel recupero dei link GitHub' });\n }\n });\n\n // \u2500\u2500 GET /api/github/repos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Lista i repository che hanno generato eventi webhook,\n // con il conteggio dei link e la data dell'ultimo evento.\n router.get('/api/github/repos', (_req: Request, res: Response) => {\n try {\n const repos = listReposWithLinkCount(ctx.db.db);\n res.json({ repos });\n } catch (err) {\n logger.error('WORKER', 'Errore query github repos', {}, err as Error);\n res.status(500).json({ error: 'Errore nel recupero dei repository' });\n }\n });\n\n return router;\n}\n", "/**\n * Router Import/Export JSONL.\n *\n * Endpoints:\n * GET /api/export \u2014 Export streaming JSONL con filtri (project, type, from, to)\n * POST /api/import \u2014 Import JSONL dal body (con dry-run opzionale)\n */\n\nimport { Router } from 'express';\nimport type { Request, Response, NextFunction } from 'express';\nimport type { WorkerContext } from '../worker-context.js';\nimport { isValidProject } from '../worker-context.js';\nimport {\n generateMetaRecord,\n exportObservationsStreaming,\n exportSummariesStreaming,\n exportPromptsStreaming,\n importJsonl,\n type ExportFilters,\n} from '../sqlite/ImportExport.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createImportExportRouter(ctx: WorkerContext): Router {\n const router = Router();\n\n // \u2500\u2500 GET /api/export \u2500\u2500\n\n /**\n * Esporta il database in formato JSONL streaming.\n *\n * Query params:\n * - project: filtra per progetto (opzionale)\n * - type: filtra per tipo observation (opzionale)\n * - from: data inizio ISO (opzionale)\n * - to: data fine ISO (opzionale)\n *\n * Risposta: text/plain con Content-Disposition: attachment\n * Prima riga: record _meta con statistiche e filtri\n * Righe seguenti: observations, summaries, prompts\n */\n router.get('/api/export', (req, res) => {\n const { project, type, from, to } = req.query as {\n project?: string;\n type?: string;\n from?: string;\n to?: string;\n };\n\n // Validazione progetto\n if (project && !isValidProject(project)) {\n res.status(400).json({ error: 'Nome progetto non valido' });\n return;\n }\n\n // Validazione date\n if (from && isNaN(new Date(from).getTime())) {\n res.status(400).json({ error: 'Parametro \"from\" non \u00E8 una data ISO valida' });\n return;\n }\n if (to && isNaN(new Date(to).getTime())) {\n res.status(400).json({ error: 'Parametro \"to\" non \u00E8 una data ISO valida' });\n return;\n }\n\n const filters: ExportFilters = {};\n if (project) filters.project = project;\n if (type) filters.type = type;\n if (from) filters.from = from;\n if (to) filters.to = to;\n\n // Nome file per download\n const dateStr = new Date().toISOString().replace(/[:.]/g, '-').substring(0, 19);\n const projectSlug = project ? `_${project.replace(/[^a-z0-9]/gi, '_')}` : '';\n const filename = `kiro-memory${projectSlug}_${dateStr}.jsonl`;\n\n try {\n // Imposta header per streaming\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\n res.setHeader('Content-Disposition', `attachment; filename=\"${filename}\"`);\n res.setHeader('Transfer-Encoding', 'chunked');\n res.setHeader('X-Content-Type-Options', 'nosniff');\n\n const db = ctx.db.db;\n\n // Prima riga: metadati\n res.write(generateMetaRecord(db, filters) + '\\n');\n\n // Export streaming observations\n exportObservationsStreaming(db, filters, (line) => {\n res.write(line + '\\n');\n });\n\n // Export streaming summaries\n exportSummariesStreaming(db, filters, (line) => {\n res.write(line + '\\n');\n });\n\n // Export streaming prompts\n exportPromptsStreaming(db, filters, (line) => {\n res.write(line + '\\n');\n });\n\n res.end();\n\n logger.info('WORKER', `Export JSONL completato: project=${project || 'all'}, from=${from || '-'}, to=${to || '-'}`);\n } catch (error) {\n logger.error('WORKER', 'Export JSONL fallito', { project, from, to }, error as Error);\n // Se gli header non sono stati inviati, rispondi con errore JSON\n if (!res.headersSent) {\n res.status(500).json({ error: 'Export JSONL fallito' });\n } else {\n res.end();\n }\n }\n });\n\n // \u2500\u2500 POST /api/import \u2500\u2500\n\n /**\n * Importa un file JSONL inviato nel body.\n *\n * Body: testo JSONL (Content-Type: text/plain oppure application/json con campo \"content\")\n *\n * Query params:\n * - dry_run=true: mostra il conteggio senza inserire\n *\n * Risposta JSON:\n * { imported, skipped, errors, total, dryRun, errorDetails[] }\n */\n router.post('/api/import', express_text_middleware, (req, res) => {\n const dryRun = req.query.dry_run === 'true' || req.query.dry_run === '1';\n\n // Recupera il contenuto JSONL dal body\n let content: string;\n\n if (typeof req.body === 'string') {\n // Content-Type: text/plain\n content = req.body;\n } else if (req.body && typeof req.body === 'object' && typeof req.body.content === 'string') {\n // Content-Type: application/json con campo \"content\"\n content = req.body.content;\n } else {\n res.status(400).json({ error: 'Body deve essere testo JSONL (text/plain) o JSON con campo \"content\"' });\n return;\n }\n\n if (!content || content.trim().length === 0) {\n res.status(400).json({ error: 'Body vuoto: nessun dato da importare' });\n return;\n }\n\n // Limite dimensione: 50MB\n const MAX_IMPORT_BYTES = 50 * 1024 * 1024;\n if (content.length > MAX_IMPORT_BYTES) {\n res.status(413).json({ error: `File troppo grande: max ${MAX_IMPORT_BYTES / 1024 / 1024}MB` });\n return;\n }\n\n try {\n const result = importJsonl(ctx.db.db, content, dryRun);\n\n logger.info('WORKER', `Import JSONL: ${result.imported} importati, ${result.skipped} saltati, ${result.errors} errori (dryRun=${dryRun})`);\n\n // Invalida cache progetti se abbiamo inserito nuovi record\n if (!dryRun && result.imported > 0) {\n ctx.invalidateProjectsCache();\n }\n\n res.json({\n success: true,\n dryRun,\n imported: result.imported,\n skipped: result.skipped,\n errors: result.errors,\n total: result.total,\n errorDetails: result.errorDetails.slice(0, 50), // max 50 dettagli di errore\n });\n } catch (error) {\n logger.error('WORKER', 'Import JSONL fallito', { dryRun }, error as Error);\n res.status(500).json({ error: 'Import JSONL fallito' });\n }\n });\n\n return router;\n}\n\n/**\n * Middleware per accettare body text/plain oltre a application/json.\n * Necessario perch\u00E9 Express di default parsa solo JSON.\n */\nfunction express_text_middleware(\n req: Request,\n res: Response,\n next: NextFunction\n): void {\n const contentType = req.headers['content-type'] || '';\n\n // Se il body \u00E8 gi\u00E0 parsato come oggetto (application/json), vai avanti\n if (typeof req.body === 'object' && req.body !== null) {\n next();\n return;\n }\n\n // Altrimenti, leggi il body come testo\n if (contentType.includes('text/plain') || contentType.includes('application/octet-stream') || !contentType) {\n const chunks: Buffer[] = [];\n req.on('data', (chunk: Buffer) => chunks.push(chunk));\n req.on('end', () => {\n req.body = Buffer.concat(chunks).toString('utf-8');\n next();\n });\n req.on('error', (err) => {\n res.status(400).json({ error: `Errore lettura body: ${err.message}` });\n });\n } else {\n next();\n }\n}\n", "/**\n * Router OpenAPI \u2014 serve la specifica e la Swagger UI interattiva.\n *\n * Endpoint esposti:\n * GET /api/docs \u2014 Swagger UI HTML (CDN, nessuna dipendenza npm aggiuntiva)\n * GET /api/docs/openapi.json \u2014 Specifica OpenAPI 3.1 in formato JSON raw\n */\n\nimport { Router } from 'express';\nimport { openApiSpec } from './spec.js';\n\n// \u2500\u2500 Generazione HTML Swagger UI \u2500\u2500\n\n/**\n * Costruisce la pagina HTML con Swagger UI caricata da CDN unpkg.\n * Non richiede dipendenze npm aggiuntive (niente swagger-ui-express).\n */\nfunction buildSwaggerHtml(specUrl: string): string {\n return `<!DOCTYPE html>\n<html lang=\"it\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Kiro Memory API \u2014 Documentazione</title>\n <meta name=\"description\" content=\"Documentazione interattiva dell'API REST di Kiro Memory\" />\n <link rel=\"stylesheet\" href=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui.css\" />\n <style>\n /* Stile minimo per rimuovere il banner Swagger */\n .swagger-ui .topbar { display: none; }\n body { margin: 0; background: #fafafa; }\n #kiro-header {\n background: #1a1a2e;\n color: #e0e0e0;\n padding: 14px 24px;\n font-family: system-ui, sans-serif;\n display: flex;\n align-items: center;\n gap: 12px;\n }\n #kiro-header h1 { margin: 0; font-size: 1.1rem; font-weight: 600; }\n #kiro-header .badge {\n background: #16213e;\n border: 1px solid #0f3460;\n border-radius: 4px;\n padding: 2px 8px;\n font-size: 0.75rem;\n color: #94a3b8;\n }\n </style>\n</head>\n<body>\n <div id=\"kiro-header\">\n <h1>Kiro Memory REST API</h1>\n <span class=\"badge\">v${openApiSpec.info.version}</span>\n <span class=\"badge\">OpenAPI 3.1</span>\n </div>\n <div id=\"swagger-ui\"></div>\n <script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js\"></script>\n <script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js\"></script>\n <script>\n window.onload = function () {\n SwaggerUIBundle({\n url: '${specUrl}',\n dom_id: '#swagger-ui',\n deepLinking: true,\n presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],\n layout: 'StandaloneLayout',\n tryItOutEnabled: true,\n requestSnippetsEnabled: true,\n displayRequestDuration: true,\n filter: true,\n defaultModelsExpandDepth: 1,\n defaultModelExpandDepth: 2,\n docExpansion: 'list'\n });\n };\n </script>\n</body>\n</html>`;\n}\n\n// \u2500\u2500 Router Factory \u2500\u2500\n\n/**\n * Crea il router Express per la documentazione OpenAPI.\n *\n * @returns Router con le route /api/docs e /api/docs/openapi.json\n */\nexport function createDocsRouter(): Router {\n const router = Router();\n\n // Spec JSON raw\n router.get('/api/docs/openapi.json', (_req, res) => {\n res.setHeader('Cache-Control', 'public, max-age=300'); // cache 5 minuti\n res.json(openApiSpec);\n });\n\n // Swagger UI HTML \u2014 importante: dopo il JSON per evitare conflitti di path matching\n router.get('/api/docs', (_req, res) => {\n const html = buildSwaggerHtml('/api/docs/openapi.json');\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.setHeader('Cache-Control', 'public, max-age=300');\n res.send(html);\n });\n\n return router;\n}\n\n// Re-export spec per usi programmatici (es. test, SDK)\nexport { openApiSpec } from './spec.js';\nexport type { OpenApiSpec } from './spec.js';\n", "/**\n * Specifica OpenAPI 3.1 per il worker REST di Kiro Memory.\n *\n * Documento hand-written che copre TUTTI gli endpoint dei router modulari:\n * - core.ts \u2192 /health, /events, POST /api/notify\n * - observations.ts \u2192 GET/POST /api/observations, POST /api/observations/batch,\n * POST /api/knowledge, POST /api/memory/save, GET /api/context/:project\n * - summaries.ts \u2192 GET/POST /api/summaries\n * - search.ts \u2192 GET /api/search, /api/hybrid-search, /api/timeline\n * - analytics.ts \u2192 GET /api/analytics/overview|timeline|types|sessions|anomalies\n * - sessions.ts \u2192 GET /api/sessions, /api/sessions/:id/checkpoint,\n * /api/checkpoint, /api/prompts\n * - projects.ts \u2192 GET /api/projects, GET/PUT /api/project-aliases,\n * GET /api/stats/:project\n * - data.ts \u2192 POST /api/embeddings/backfill, GET /api/embeddings/stats,\n * POST /api/retention/cleanup, GET /api/export, GET /api/report\n */\n\n// \u2500\u2500 Componenti riutilizzabili \u2500\u2500\n\n/** Schema di errore standard */\nconst ErrorSchema = {\n type: 'object',\n properties: {\n error: { type: 'string', description: 'Messaggio di errore leggibile' }\n },\n required: ['error']\n} as const;\n\n/** Schema osservazione completo (row SQLite) */\nconst ObservationSchema = {\n type: 'object',\n properties: {\n id: { type: 'integer' },\n session_id: { type: 'string' },\n project: { type: 'string' },\n type: { type: 'string' },\n title: { type: 'string' },\n narrative: { type: ['string', 'null'] },\n content: { type: ['string', 'null'] },\n summary: { type: ['string', 'null'] },\n metadata: { type: ['string', 'null'] },\n concepts: { type: ['string', 'null'] },\n files: { type: ['string', 'null'] },\n raw_payload: { type: ['string', 'null'] },\n discovery_tokens: { type: 'integer' },\n created_at_epoch: { type: 'integer' }\n }\n} as const;\n\n/** Schema riassunto sessione */\nconst SummarySchema = {\n type: 'object',\n properties: {\n id: { type: 'integer' },\n session_id: { type: 'string' },\n project: { type: 'string' },\n request: { type: ['string', 'null'] },\n learned: { type: ['string', 'null'] },\n completed: { type: ['string', 'null'] },\n next_steps: { type: ['string', 'null'] },\n raw_payload: { type: ['string', 'null'] },\n created_at_epoch: { type: 'integer' }\n }\n} as const;\n\n/** Schema sessione */\nconst SessionSchema = {\n type: 'object',\n properties: {\n id: { type: 'integer' },\n session_id: { type: 'string' },\n project: { type: 'string' },\n started_at_epoch: { type: 'integer' },\n ended_at_epoch: { type: ['integer', 'null'] }\n }\n} as const;\n\n/** Schema prompt utente */\nconst PromptSchema = {\n type: 'object',\n properties: {\n id: { type: 'integer' },\n session_id: { type: 'string' },\n project: { type: 'string' },\n prompt: { type: 'string' },\n created_at_epoch: { type: 'integer' }\n }\n} as const;\n\n/** Risposta paginata con header X-Total-Count */\nfunction paginatedResponse(itemSchema: object) {\n return {\n description: 'Lista paginata. Il totale \u00E8 incluso nell\\'header X-Total-Count.',\n headers: {\n 'X-Total-Count': {\n schema: { type: 'integer' },\n description: 'Numero totale di record nel database'\n }\n },\n content: {\n 'application/json': {\n schema: {\n type: 'array',\n items: itemSchema\n }\n }\n }\n };\n}\n\n/** Risposta errore standard */\nfunction errorResponse(description: string) {\n return {\n description,\n content: {\n 'application/json': {\n schema: ErrorSchema\n }\n }\n };\n}\n\n// \u2500\u2500 Specifica OpenAPI 3.1 \u2500\u2500\n\nexport const openApiSpec = {\n openapi: '3.1.0',\n info: {\n title: 'Kiro Memory REST API',\n version: '3.0.0',\n description: [\n 'API REST del worker Kiro Memory (porta 3001).',\n 'Fornisce accesso a osservazioni, sommari, sessioni, ricerca,',\n 'analytics, export, embeddings vettoriali e gestione progetti.'\n ].join(' '),\n license: { name: 'MIT' },\n contact: {\n name: 'Auriti-Labs',\n url: 'https://github.com/Auriti-Labs/kiro-memory'\n }\n },\n servers: [\n { url: 'http://127.0.0.1:3001', description: 'Worker locale (default)' }\n ],\n\n // \u2500\u2500 Componenti condivisi \u2500\u2500\n components: {\n schemas: {\n Error: ErrorSchema,\n Observation: ObservationSchema,\n Summary: SummarySchema,\n Session: SessionSchema,\n Prompt: PromptSchema,\n Checkpoint: {\n type: 'object',\n properties: {\n id: { type: 'integer' },\n session_id: { type: 'integer' },\n project: { type: 'string' },\n content: { type: 'string' },\n created_at_epoch: { type: 'integer' }\n }\n },\n AnalyticsOverview: {\n type: 'object',\n properties: {\n totalObservations: { type: 'integer' },\n totalSummaries: { type: 'integer' },\n totalPrompts: { type: 'integer' },\n totalSessions: { type: 'integer' },\n projects: { type: 'array', items: { type: 'string' } }\n }\n },\n TimelineEntry: {\n type: 'object',\n properties: {\n date: { type: 'string', format: 'date' },\n count: { type: 'integer' }\n }\n },\n TypeDistributionEntry: {\n type: 'object',\n properties: {\n type: { type: 'string' },\n count: { type: 'integer' }\n }\n },\n SessionStats: {\n type: 'object',\n properties: {\n totalSessions: { type: 'integer' },\n avgObsPerSession: { type: 'number' },\n avgDurationMs: { type: 'number' }\n }\n },\n AnomalyEntry: {\n type: 'object',\n properties: {\n sessionId: { type: 'integer' },\n project: { type: 'string' },\n obsCount: { type: 'integer' },\n zScore: { type: 'number' },\n isAnomaly: { type: 'boolean' }\n }\n },\n EmbeddingStats: {\n type: 'object',\n properties: {\n total: { type: 'integer' },\n withEmbed: { type: 'integer' },\n provider: { type: 'string' },\n dimensions:{ type: 'integer' },\n available: { type: 'boolean' }\n }\n },\n HybridSearchResult: {\n type: 'object',\n properties: {\n id: { type: 'integer' },\n project: { type: 'string' },\n type: { type: 'string' },\n title: { type: 'string' },\n score: { type: 'number' },\n source: { type: 'string', enum: ['vector', 'fts', 'hybrid'] }\n }\n },\n ProjectAlias: {\n type: 'object',\n properties: {\n project_name: { type: 'string' },\n display_name: { type: 'string' }\n }\n },\n ProjectStats: {\n type: 'object',\n properties: {\n project: { type: 'string' },\n observations: { type: 'integer' },\n summaries: { type: 'integer' },\n prompts: { type: 'integer' },\n sessions: { type: 'integer' },\n lastActivity: { type: 'integer', description: 'epoch ms' }\n }\n },\n ExportData: {\n type: 'object',\n properties: {\n meta: {\n type: 'object',\n properties: {\n project: { type: 'string' },\n daysBack: { type: 'integer' },\n exportedAt: { type: 'string', format: 'date-time' }\n }\n },\n observations: { type: 'array', items: ObservationSchema },\n summaries: { type: 'array', items: SummarySchema }\n }\n }\n },\n securitySchemes: {\n workerToken: {\n type: 'apiKey',\n in: 'header',\n name: 'X-Worker-Token',\n description: 'Token segreto per endpoint riservati (notify, backfill, cleanup)'\n }\n },\n parameters: {\n projectQuery: {\n name: 'project',\n in: 'query',\n schema: { type: 'string' },\n description: 'Filtra per nome progetto'\n },\n offsetQuery: {\n name: 'offset',\n in: 'query',\n schema: { type: 'integer', default: 0, minimum: 0 },\n description: 'Offset paginazione'\n },\n limitQuery: {\n name: 'limit',\n in: 'query',\n schema: { type: 'integer', default: 20, minimum: 1, maximum: 200 },\n description: 'Numero massimo di risultati'\n }\n }\n },\n\n // \u2500\u2500 Path definitions \u2500\u2500\n paths: {\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n // CORE\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n '/health': {\n get: {\n tags: ['Core'],\n summary: 'Health check',\n description: 'Verifica che il worker sia attivo e restituisce la versione.',\n operationId: 'getHealth',\n responses: {\n '200': {\n description: 'Worker attivo',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n status: { type: 'string', example: 'ok' },\n timestamp: { type: 'integer', description: 'epoch ms' },\n version: { type: 'string', example: '2.1.0' }\n }\n }\n }\n }\n }\n }\n }\n },\n\n '/events': {\n get: {\n tags: ['Core'],\n summary: 'Server-Sent Events (SSE)',\n description: [\n 'Stream SSE per aggiornamenti in tempo reale alla dashboard.',\n 'Emette eventi: `connected`, `observation-created`, `summary-created`,',\n '`prompt-created`, `session-created`. Keepalive ogni 15s.',\n 'Limite: 50 connessioni simultanee.'\n ].join(' '),\n operationId: 'getEvents',\n responses: {\n '200': {\n description: 'Stream SSE attivo',\n content: { 'text/event-stream': { schema: { type: 'string' } } }\n },\n '503': errorResponse('Troppi client SSE connessi')\n }\n }\n },\n\n '/api/notify': {\n post: {\n tags: ['Core'],\n summary: 'Notifica interna hook \u2192 SSE',\n description: [\n 'Endpoint riservato agli hook Kiro. Riceve un evento e lo broadcast',\n 'a tutti i client SSE connessi. Richiede `X-Worker-Token`.',\n 'Rate limit: 60 richieste/minuto.'\n ].join(' '),\n operationId: 'postNotify',\n security: [{ workerToken: [] }],\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n required: ['event'],\n properties: {\n event: {\n type: 'string',\n enum: ['observation-created', 'summary-created', 'prompt-created', 'session-created']\n },\n data: { type: 'object', additionalProperties: true }\n }\n }\n }\n }\n },\n responses: {\n '200': {\n description: 'Evento broadcast con successo',\n content: { 'application/json': { schema: { type: 'object', properties: { ok: { type: 'boolean' } } } } }\n },\n '400': errorResponse('Evento non valido o non consentito'),\n '401': errorResponse('Token mancante o non valido')\n }\n }\n },\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n // OBSERVATIONS\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n '/api/observations': {\n get: {\n tags: ['Observations'],\n summary: 'Lista osservazioni paginate',\n operationId: 'listObservations',\n parameters: [\n { $ref: '#/components/parameters/offsetQuery' },\n { $ref: '#/components/parameters/limitQuery' },\n { $ref: '#/components/parameters/projectQuery' }\n ],\n responses: {\n '200': paginatedResponse({ $ref: '#/components/schemas/Observation' }),\n '500': errorResponse('Errore interno')\n }\n },\n post: {\n tags: ['Observations'],\n summary: 'Crea una nuova osservazione',\n operationId: 'createObservation',\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n required: ['project', 'title'],\n properties: {\n memorySessionId: { type: 'string' },\n project: { type: 'string' },\n type: { type: 'string', default: 'manual' },\n title: { type: 'string', maxLength: 500 },\n content: { type: 'string', maxLength: 100000 },\n concepts: { type: 'array', items: { type: 'string' } },\n files: { type: 'array', items: { type: 'string' } }\n }\n }\n }\n }\n },\n responses: {\n '200': {\n description: 'Osservazione creata',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n id: { type: 'integer' },\n success: { type: 'boolean' }\n }\n }\n }\n }\n },\n '400': errorResponse('Payload non valido'),\n '500': errorResponse('Errore di persistenza')\n }\n }\n },\n\n '/api/observations/batch': {\n post: {\n tags: ['Observations'],\n summary: 'Recupero batch per ID (max 100)',\n operationId: 'batchGetObservations',\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n required: ['ids'],\n properties: {\n ids: {\n type: 'array',\n items: { type: 'integer', minimum: 1 },\n minItems: 1,\n maxItems: 100\n }\n }\n }\n }\n }\n },\n responses: {\n '200': {\n description: 'Osservazioni trovate',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n observations: {\n type: 'array',\n items: { $ref: '#/components/schemas/Observation' }\n }\n }\n }\n }\n }\n },\n '400': errorResponse('Array ids non valido'),\n '500': errorResponse('Errore interno')\n }\n }\n },\n\n '/api/knowledge': {\n post: {\n tags: ['Observations'],\n summary: 'Salva conoscenza strutturata',\n description: [\n 'Crea un\\'osservazione di tipo knowledge con metadati tipizzati.',\n 'Tipi supportati: `constraint`, `decision`, `heuristic`, `rejected`.'\n ].join(' '),\n operationId: 'createKnowledge',\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n required: ['project', 'knowledge_type', 'title', 'content'],\n properties: {\n project: { type: 'string' },\n knowledge_type: { type: 'string', enum: ['constraint', 'decision', 'heuristic', 'rejected'] },\n title: { type: 'string', maxLength: 500 },\n content: { type: 'string', maxLength: 100000 },\n concepts: { type: 'array', items: { type: 'string' } },\n files: { type: 'array', items: { type: 'string' } },\n severity: { type: 'string', enum: ['hard', 'soft'], description: 'Solo per constraint' },\n alternatives: { type: 'array', items: { type: 'string' }, description: 'Solo per decision/rejected' },\n reason: { type: 'string', description: 'Motivazione (constraint, decision, rejected)' },\n context: { type: 'string', description: 'Contesto applicativo (heuristic)' },\n confidence: { type: 'string', enum: ['high', 'medium', 'low'], description: 'Solo per heuristic' }\n }\n }\n }\n }\n },\n responses: {\n '200': {\n description: 'Conoscenza salvata',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n id: { type: 'integer' },\n success: { type: 'boolean' },\n knowledge_type: { type: 'string' }\n }\n }\n }\n }\n },\n '400': errorResponse('Payload non valido o knowledge_type sconosciuto'),\n '500': errorResponse('Errore di persistenza')\n }\n }\n },\n\n '/api/memory/save': {\n post: {\n tags: ['Observations'],\n summary: 'Salva una memoria (endpoint programmabile)',\n operationId: 'saveMemory',\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n required: ['project', 'title', 'content'],\n properties: {\n project: { type: 'string' },\n title: { type: 'string', maxLength: 500 },\n content: { type: 'string', maxLength: 100000 },\n type: { type: 'string', default: 'research' },\n concepts: { type: ['array', 'string'], items: { type: 'string' } }\n }\n }\n }\n }\n },\n responses: {\n '200': {\n description: 'Memoria salvata',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n id: { type: 'integer' },\n success: { type: 'boolean' }\n }\n }\n }\n }\n },\n '400': errorResponse('Campo obbligatorio mancante o non valido'),\n '500': errorResponse('Errore di persistenza')\n }\n }\n },\n\n '/api/context/{project}': {\n get: {\n tags: ['Observations'],\n summary: 'Contesto progetto (ultime osservazioni + sommari)',\n operationId: 'getProjectContext',\n parameters: [\n {\n name: 'project',\n in: 'path',\n required: true,\n schema: { type: 'string' },\n description: 'Nome del progetto'\n }\n ],\n responses: {\n '200': {\n description: 'Contesto del progetto',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n project: { type: 'string' },\n observations: { type: 'array', items: { $ref: '#/components/schemas/Observation' } },\n summaries: { type: 'array', items: { $ref: '#/components/schemas/Summary' } }\n }\n }\n }\n }\n },\n '400': errorResponse('Nome progetto non valido'),\n '500': errorResponse('Errore interno')\n }\n }\n },\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n // SUMMARIES\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n '/api/summaries': {\n get: {\n tags: ['Summaries'],\n summary: 'Lista sommari paginata',\n operationId: 'listSummaries',\n parameters: [\n { $ref: '#/components/parameters/offsetQuery' },\n { $ref: '#/components/parameters/limitQuery' },\n { $ref: '#/components/parameters/projectQuery' }\n ],\n responses: {\n '200': paginatedResponse({ $ref: '#/components/schemas/Summary' }),\n '500': errorResponse('Errore interno')\n }\n },\n post: {\n tags: ['Summaries'],\n summary: 'Crea un nuovo sommario di sessione',\n operationId: 'createSummary',\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n required: ['project'],\n properties: {\n sessionId: { type: 'string' },\n project: { type: 'string' },\n request: { type: 'string', maxLength: 50000 },\n learned: { type: 'string', maxLength: 50000 },\n completed: { type: 'string', maxLength: 50000 },\n nextSteps: { type: 'string', maxLength: 50000 }\n }\n }\n }\n }\n },\n responses: {\n '200': {\n description: 'Sommario creato',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n id: { type: 'integer' },\n success: { type: 'boolean' }\n }\n }\n }\n }\n },\n '400': errorResponse('Payload non valido o campo troppo grande'),\n '500': errorResponse('Errore di persistenza')\n }\n }\n },\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n // SEARCH\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n '/api/search': {\n get: {\n tags: ['Search'],\n summary: 'Ricerca full-text FTS5',\n operationId: 'searchFTS',\n parameters: [\n {\n name: 'q',\n in: 'query',\n required: true,\n schema: { type: 'string' },\n description: 'Testo da cercare (SQLite FTS5)'\n },\n { $ref: '#/components/parameters/projectQuery' },\n {\n name: 'type',\n in: 'query',\n schema: { type: 'string' },\n description: 'Filtra per tipo osservazione'\n },\n {\n name: 'limit',\n in: 'query',\n schema: { type: 'integer', default: 20, minimum: 1, maximum: 100 }\n }\n ],\n responses: {\n '200': {\n description: 'Risultati ricerca FTS5',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n observations: { type: 'array', items: { $ref: '#/components/schemas/Observation' } },\n summaries: { type: 'array', items: { $ref: '#/components/schemas/Summary' } }\n }\n }\n }\n }\n },\n '400': errorResponse('Parametro \"q\" obbligatorio'),\n '500': errorResponse('Ricerca fallita')\n }\n }\n },\n\n '/api/hybrid-search': {\n get: {\n tags: ['Search'],\n summary: 'Ricerca ibrida (vettoriale + keyword)',\n description: 'Combina embedding vettoriali e FTS5 per una ricerca semantica avanzata.',\n operationId: 'searchHybrid',\n parameters: [\n {\n name: 'q',\n in: 'query',\n required: true,\n schema: { type: 'string' },\n description: 'Testo da cercare'\n },\n { $ref: '#/components/parameters/projectQuery' },\n {\n name: 'limit',\n in: 'query',\n schema: { type: 'integer', default: 10, minimum: 1, maximum: 100 }\n }\n ],\n responses: {\n '200': {\n description: 'Risultati ibridi con score di rilevanza',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n results: { type: 'array', items: { $ref: '#/components/schemas/HybridSearchResult' } },\n count: { type: 'integer' }\n }\n }\n }\n }\n },\n '400': errorResponse('Parametro \"q\" obbligatorio'),\n '500': errorResponse('Ricerca ibrida fallita')\n }\n }\n },\n\n '/api/timeline': {\n get: {\n tags: ['Search'],\n summary: 'Timeline cronologica intorno a un\\'osservazione',\n operationId: 'getTimeline',\n parameters: [\n {\n name: 'anchor',\n in: 'query',\n required: true,\n schema: { type: 'integer', minimum: 1 },\n description: 'ID dell\\'osservazione centrale'\n },\n {\n name: 'depth_before',\n in: 'query',\n schema: { type: 'integer', default: 5, minimum: 1, maximum: 50 },\n description: 'Numero di osservazioni precedenti'\n },\n {\n name: 'depth_after',\n in: 'query',\n schema: { type: 'integer', default: 5, minimum: 1, maximum: 50 },\n description: 'Numero di osservazioni successive'\n }\n ],\n responses: {\n '200': {\n description: 'Timeline restituita',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n timeline: { type: 'array', items: { $ref: '#/components/schemas/Observation' } }\n }\n }\n }\n }\n },\n '400': errorResponse('Parametro \"anchor\" mancante o non valido'),\n '500': errorResponse('Timeline fallita')\n }\n }\n },\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n // ANALYTICS\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n '/api/analytics/overview': {\n get: {\n tags: ['Analytics'],\n summary: 'Panoramica aggregata delle statistiche',\n operationId: 'getAnalyticsOverview',\n parameters: [{ $ref: '#/components/parameters/projectQuery' }],\n responses: {\n '200': {\n description: 'Overview aggregata',\n content: {\n 'application/json': {\n schema: { $ref: '#/components/schemas/AnalyticsOverview' }\n }\n }\n },\n '400': errorResponse('Nome progetto non valido'),\n '500': errorResponse('Analytics overview fallita')\n }\n }\n },\n\n '/api/analytics/timeline': {\n get: {\n tags: ['Analytics'],\n summary: 'Distribuzione temporale osservazioni per giorno',\n operationId: 'getAnalyticsTimeline',\n parameters: [\n { $ref: '#/components/parameters/projectQuery' },\n {\n name: 'days',\n in: 'query',\n schema: { type: 'integer', default: 30, minimum: 1, maximum: 365 },\n description: 'Finestra temporale in giorni'\n }\n ],\n responses: {\n '200': {\n description: 'Serie temporale giornaliera',\n content: {\n 'application/json': {\n schema: { type: 'array', items: { $ref: '#/components/schemas/TimelineEntry' } }\n }\n }\n },\n '400': errorResponse('Nome progetto non valido'),\n '500': errorResponse('Analytics timeline fallita')\n }\n }\n },\n\n '/api/analytics/types': {\n get: {\n tags: ['Analytics'],\n summary: 'Distribuzione per tipo osservazione',\n operationId: 'getAnalyticsTypes',\n parameters: [{ $ref: '#/components/parameters/projectQuery' }],\n responses: {\n '200': {\n description: 'Distribuzione dei tipi',\n content: {\n 'application/json': {\n schema: { type: 'array', items: { $ref: '#/components/schemas/TypeDistributionEntry' } }\n }\n }\n },\n '400': errorResponse('Nome progetto non valido'),\n '500': errorResponse('Analytics types fallita')\n }\n }\n },\n\n '/api/analytics/sessions': {\n get: {\n tags: ['Analytics'],\n summary: 'Statistiche aggregate delle sessioni',\n operationId: 'getAnalyticsSessions',\n parameters: [{ $ref: '#/components/parameters/projectQuery' }],\n responses: {\n '200': {\n description: 'Statistiche sessioni',\n content: {\n 'application/json': {\n schema: { $ref: '#/components/schemas/SessionStats' }\n }\n }\n },\n '400': errorResponse('Nome progetto non valido'),\n '500': errorResponse('Analytics sessions fallita')\n }\n }\n },\n\n '/api/analytics/anomalies': {\n get: {\n tags: ['Analytics'],\n summary: 'Rilevamento anomalie sessioni tramite z-score',\n operationId: 'getAnomalies',\n parameters: [\n {\n name: 'project',\n in: 'query',\n required: true,\n schema: { type: 'string' },\n description: 'Nome progetto (obbligatorio)'\n },\n {\n name: 'window',\n in: 'query',\n schema: { type: 'integer', default: 20, minimum: 3, maximum: 200 },\n description: 'Dimensione finestra rolling'\n },\n {\n name: 'threshold',\n in: 'query',\n schema: { type: 'number', default: 2.0, minimum: 0.1, maximum: 10 },\n description: 'Soglia z-score per segnalare anomalia'\n }\n ],\n responses: {\n '200': {\n description: 'Anomalie rilevate',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n anomalies: { type: 'array', items: { $ref: '#/components/schemas/AnomalyEntry' } },\n baseline: { type: 'number' },\n project: { type: 'string' }\n }\n }\n }\n }\n },\n '400': errorResponse('Parametro project obbligatorio o threshold non valida'),\n '500': errorResponse('Rilevamento anomalie fallito')\n }\n }\n },\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n // SESSIONS\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n '/api/sessions': {\n get: {\n tags: ['Sessions'],\n summary: 'Lista sessioni (max 50)',\n operationId: 'listSessions',\n parameters: [{ $ref: '#/components/parameters/projectQuery' }],\n responses: {\n '200': {\n description: 'Lista sessioni',\n content: {\n 'application/json': {\n schema: { type: 'array', items: { $ref: '#/components/schemas/Session' } }\n }\n }\n },\n '400': errorResponse('Nome progetto non valido'),\n '500': errorResponse('Errore interno')\n }\n }\n },\n\n '/api/sessions/{id}/checkpoint': {\n get: {\n tags: ['Sessions'],\n summary: 'Ultimo checkpoint di una sessione',\n operationId: 'getSessionCheckpoint',\n parameters: [\n {\n name: 'id',\n in: 'path',\n required: true,\n schema: { type: 'integer', minimum: 1 },\n description: 'ID numerico della sessione'\n }\n ],\n responses: {\n '200': {\n description: 'Checkpoint trovato',\n content: {\n 'application/json': {\n schema: { $ref: '#/components/schemas/Checkpoint' }\n }\n }\n },\n '400': errorResponse('ID sessione non valido'),\n '404': errorResponse('Nessun checkpoint trovato per questa sessione'),\n '500': errorResponse('Errore interno')\n }\n }\n },\n\n '/api/checkpoint': {\n get: {\n tags: ['Sessions'],\n summary: 'Ultimo checkpoint del progetto',\n operationId: 'getProjectCheckpoint',\n parameters: [\n {\n name: 'project',\n in: 'query',\n required: true,\n schema: { type: 'string' },\n description: 'Nome progetto (obbligatorio)'\n }\n ],\n responses: {\n '200': {\n description: 'Checkpoint trovato',\n content: {\n 'application/json': {\n schema: { $ref: '#/components/schemas/Checkpoint' }\n }\n }\n },\n '400': errorResponse('Parametro project mancante o non valido'),\n '404': errorResponse('Nessun checkpoint trovato per questo progetto'),\n '500': errorResponse('Errore interno')\n }\n }\n },\n\n '/api/prompts': {\n get: {\n tags: ['Sessions'],\n summary: 'Lista prompt utente paginata',\n operationId: 'listPrompts',\n parameters: [\n { $ref: '#/components/parameters/offsetQuery' },\n {\n name: 'limit',\n in: 'query',\n schema: { type: 'integer', default: 20, minimum: 1, maximum: 200 }\n },\n { $ref: '#/components/parameters/projectQuery' }\n ],\n responses: {\n '200': paginatedResponse({ $ref: '#/components/schemas/Prompt' }),\n '500': errorResponse('Errore interno')\n }\n }\n },\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n // PROJECTS\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n '/api/projects': {\n get: {\n tags: ['Projects'],\n summary: 'Lista nomi progetto distinti (cache 60s)',\n operationId: 'listProjects',\n responses: {\n '200': {\n description: 'Array di nomi progetto',\n content: {\n 'application/json': {\n schema: { type: 'array', items: { type: 'string' } }\n }\n }\n },\n '500': errorResponse('Errore interno')\n }\n }\n },\n\n '/api/project-aliases': {\n get: {\n tags: ['Projects'],\n summary: 'Mappa alias \u2192 display name',\n operationId: 'getProjectAliases',\n responses: {\n '200': {\n description: 'Oggetto `{ project_name: display_name }`',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n additionalProperties: { type: 'string' }\n }\n }\n }\n },\n '500': errorResponse('Errore interno')\n }\n }\n },\n\n '/api/project-aliases/{project}': {\n put: {\n tags: ['Projects'],\n summary: 'Crea o aggiorna display name di un progetto',\n operationId: 'upsertProjectAlias',\n parameters: [\n {\n name: 'project',\n in: 'path',\n required: true,\n schema: { type: 'string' },\n description: 'Nome tecnico del progetto'\n }\n ],\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n required: ['displayName'],\n properties: {\n displayName: { type: 'string', maxLength: 100 }\n }\n }\n }\n }\n },\n responses: {\n '200': {\n description: 'Alias aggiornato',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n ok: { type: 'boolean' },\n project_name: { type: 'string' },\n display_name: { type: 'string' }\n }\n }\n }\n }\n },\n '400': errorResponse('Payload non valido o nome progetto non valido'),\n '500': errorResponse('Errore interno')\n }\n }\n },\n\n '/api/stats/{project}': {\n get: {\n tags: ['Projects'],\n summary: 'Statistiche aggregate per progetto',\n operationId: 'getProjectStats',\n parameters: [\n {\n name: 'project',\n in: 'path',\n required: true,\n schema: { type: 'string' },\n description: 'Nome del progetto'\n }\n ],\n responses: {\n '200': {\n description: 'Statistiche progetto',\n content: {\n 'application/json': {\n schema: { $ref: '#/components/schemas/ProjectStats' }\n }\n }\n },\n '400': errorResponse('Nome progetto non valido'),\n '500': errorResponse('Errore interno')\n }\n }\n },\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n // DATA (embeddings, retention, export, report)\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n '/api/embeddings/backfill': {\n post: {\n tags: ['Data'],\n summary: 'Backfill embedding per osservazioni senza vettore',\n operationId: 'backfillEmbeddings',\n security: [{ workerToken: [] }],\n requestBody: {\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n batchSize: { type: 'integer', default: 50, minimum: 1, maximum: 500 }\n }\n }\n }\n }\n },\n responses: {\n '200': {\n description: 'Backfill completato',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n success: { type: 'boolean' },\n generated: { type: 'integer', description: 'Embedding generati' }\n }\n }\n }\n }\n },\n '401': errorResponse('Token non valido'),\n '500': errorResponse('Backfill fallito')\n }\n }\n },\n\n '/api/embeddings/stats': {\n get: {\n tags: ['Data'],\n summary: 'Statistiche embedding vettoriali',\n operationId: 'getEmbeddingStats',\n responses: {\n '200': {\n description: 'Statistiche embedding',\n content: {\n 'application/json': {\n schema: { $ref: '#/components/schemas/EmbeddingStats' }\n }\n }\n },\n '500': errorResponse('Errore interno')\n }\n }\n },\n\n '/api/retention/cleanup': {\n post: {\n tags: ['Data'],\n summary: 'Pulizia dati secondo policy di retention',\n description: 'Elimina osservazioni, sommari e prompt pi\u00F9 vecchi di `maxAgeDays` giorni. Supporta modalit\u00E0 dry-run.',\n operationId: 'runRetentionCleanup',\n security: [{ workerToken: [] }],\n requestBody: {\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n maxAgeDays: { type: 'integer', default: 90, minimum: 7, maximum: 730,\n description: 'Et\u00E0 massima in giorni' },\n dryRun: { type: 'boolean', default: false,\n description: 'Se true, simula senza eliminare' }\n }\n }\n }\n }\n },\n responses: {\n '200': {\n description: 'Cleanup eseguito o simulato',\n content: {\n 'application/json': {\n schema: {\n oneOf: [\n {\n description: 'Risultato dry-run',\n type: 'object',\n properties: {\n dryRun: { type: 'boolean' },\n maxAgeDays: { type: 'integer' },\n wouldDelete: {\n type: 'object',\n properties: {\n observations: { type: 'integer' },\n summaries: { type: 'integer' },\n prompts: { type: 'integer' }\n }\n }\n }\n },\n {\n description: 'Risultato cleanup reale',\n type: 'object',\n properties: {\n success: { type: 'boolean' },\n maxAgeDays: { type: 'integer' },\n deleted: {\n type: 'object',\n properties: {\n observations: { type: 'integer' },\n summaries: { type: 'integer' },\n prompts: { type: 'integer' }\n }\n }\n }\n }\n ]\n }\n }\n }\n },\n '401': errorResponse('Token non valido'),\n '500': errorResponse('Cleanup fallito')\n }\n }\n },\n\n '/api/export': {\n get: {\n tags: ['Data'],\n summary: 'Esporta osservazioni e sommari',\n description: 'Esporta i dati in formato JSON (default) o Markdown.',\n operationId: 'exportData',\n parameters: [\n { $ref: '#/components/parameters/projectQuery' },\n {\n name: 'format',\n in: 'query',\n schema: { type: 'string', enum: ['json', 'markdown', 'md'], default: 'json' },\n description: 'Formato di output'\n },\n {\n name: 'type',\n in: 'query',\n schema: { type: 'string' },\n description: 'Filtra per tipo osservazione'\n },\n {\n name: 'days',\n in: 'query',\n schema: { type: 'integer', default: 30, minimum: 1, maximum: 365 },\n description: 'Finestra temporale in giorni'\n }\n ],\n responses: {\n '200': {\n description: 'Export in formato selezionato',\n content: {\n 'application/json': { schema: { $ref: '#/components/schemas/ExportData' } },\n 'text/markdown': { schema: { type: 'string' } }\n }\n },\n '400': errorResponse('Nome progetto non valido'),\n '500': errorResponse('Export fallito')\n }\n }\n },\n\n '/api/report': {\n get: {\n tags: ['Data'],\n summary: 'Report attivit\u00E0 (weekly/monthly)',\n operationId: 'getReport',\n parameters: [\n { $ref: '#/components/parameters/projectQuery' },\n {\n name: 'period',\n in: 'query',\n schema: { type: 'string', enum: ['weekly', 'monthly'], default: 'weekly' },\n description: 'Periodo del report'\n },\n {\n name: 'format',\n in: 'query',\n schema: { type: 'string', enum: ['json', 'markdown', 'md', 'text'], default: 'json' },\n description: 'Formato di output'\n }\n ],\n responses: {\n '200': {\n description: 'Report generato',\n content: {\n 'application/json': { schema: { type: 'object', additionalProperties: true } },\n 'text/markdown': { schema: { type: 'string' } },\n 'text/plain': { schema: { type: 'string' } }\n }\n },\n '400': errorResponse('Nome progetto non valido'),\n '500': errorResponse('Generazione report fallita')\n }\n }\n },\n\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n // DOCS (auto-referenziale)\n // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n '/api/docs': {\n get: {\n tags: ['Docs'],\n summary: 'Swagger UI interattiva',\n operationId: 'getSwaggerUI',\n responses: {\n '200': {\n description: 'Pagina HTML Swagger UI',\n content: { 'text/html': { schema: { type: 'string' } } }\n }\n }\n }\n },\n\n '/api/docs/openapi.json': {\n get: {\n tags: ['Docs'],\n summary: 'Specifica OpenAPI 3.1 in formato JSON',\n operationId: 'getOpenApiSpec',\n responses: {\n '200': {\n description: 'Specifica OpenAPI 3.1',\n content: {\n 'application/json': { schema: { type: 'object', additionalProperties: true } }\n }\n }\n }\n }\n }\n },\n\n // \u2500\u2500 Tags per raggruppamento nella UI \u2500\u2500\n tags: [\n { name: 'Core', description: 'Health check, SSE, notifiche interne' },\n { name: 'Observations', description: 'CRUD osservazioni, knowledge, memoria' },\n { name: 'Summaries', description: 'Sommari di sessione' },\n { name: 'Search', description: 'Ricerca FTS5, vettoriale ibrida, timeline' },\n { name: 'Analytics', description: 'Overview, distribuzione, anomalie' },\n { name: 'Sessions', description: 'Sessioni, checkpoint, prompt' },\n { name: 'Projects', description: 'Lista progetti, alias, statistiche' },\n { name: 'Data', description: 'Embedding, retention, export, report' },\n { name: 'Docs', description: 'Documentazione API interattiva' }\n ]\n} as const;\n\nexport type OpenApiSpec = typeof openApiSpec;\n", "/**\n * Router Backup: gestione backup database SQLite.\n *\n * Endpoints:\n * GET /api/backup/list \u2014 Elenca backup con metadata\n * POST /api/backup/create \u2014 Crea backup manuale\n * POST /api/backup/restore \u2014 Ripristina da backup (richiede X-Worker-Token)\n */\n\nimport { Router } from 'express';\nimport type { Request, Response, NextFunction } from 'express';\nimport { join } from 'path';\nimport type { WorkerContext } from '../worker-context.js';\nimport { createBackup, listBackups, restoreBackup, rotateBackups } from '../sqlite/Backup.js';\nimport { getConfigValue } from '../../cli/cli-utils.js';\nimport { DB_PATH, BACKUPS_DIR } from '../../shared/paths.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createBackupRouter(ctx: WorkerContext, workerToken?: string): Router {\n const router = Router();\n\n /** Middleware: richiede X-Worker-Token per operazioni distruttive */\n function requireAuth(\n req: Request,\n res: Response,\n next: NextFunction\n ): void {\n if (!workerToken) { next(); return; }\n const token = req.headers['x-worker-token'] as string;\n if (token !== workerToken) {\n res.status(401).json({ error: 'X-Worker-Token non valido o mancante' });\n return;\n }\n next();\n }\n\n // \u2500\u2500 GET /api/backup/list \u2500\u2500\n\n /**\n * Elenca i backup presenti nella directory backup.\n * Ritorna array ordinato dal pi\u00F9 recente al pi\u00F9 vecchio.\n */\n router.get('/api/backup/list', (_req, res) => {\n try {\n const entries = listBackups(BACKUPS_DIR);\n const items = entries.map(e => ({\n filename: e.metadata.filename,\n timestamp: e.metadata.timestamp,\n timestampEpoch: e.metadata.timestampEpoch,\n schemaVersion: e.metadata.schemaVersion,\n stats: e.metadata.stats,\n filePath: e.filePath,\n }));\n res.json({ backups: items, total: items.length, backupDir: BACKUPS_DIR });\n } catch (error) {\n logger.error('BACKUP', 'Elenco backup fallito', {}, error as Error);\n res.status(500).json({ error: 'Impossibile elencare i backup' });\n }\n });\n\n // \u2500\u2500 POST /api/backup/create \u2500\u2500\n\n /**\n * Crea un backup manuale del database.\n * Esegue automaticamente la rotazione dopo la creazione.\n */\n router.post('/api/backup/create', (_req, res) => {\n try {\n const maxKeep = Number(getConfigValue('backup.maxKeep')) || 7;\n const entry = createBackup(DB_PATH, BACKUPS_DIR, ctx.db.db);\n\n // Rotazione automatica dopo la creazione\n const deleted = rotateBackups(BACKUPS_DIR, maxKeep);\n\n logger.info('BACKUP', `Backup manuale creato: ${entry.metadata.filename}`);\n res.json({\n success: true,\n filename: entry.metadata.filename,\n timestamp: entry.metadata.timestamp,\n stats: entry.metadata.stats,\n rotated: deleted,\n });\n } catch (error) {\n logger.error('BACKUP', 'Creazione backup fallita', {}, error as Error);\n res.status(500).json({ error: 'Creazione backup fallita' });\n }\n });\n\n // \u2500\u2500 POST /api/backup/restore \u2500\u2500\n\n /**\n * Ripristina il database da un file di backup.\n *\n * Body: { \"file\": \"backup-2026-02-27-150000.db\" }\n *\n * ATTENZIONE: questa operazione sovrascrive il database corrente.\n * Richiede X-Worker-Token per autorizzazione.\n */\n router.post('/api/backup/restore', requireAuth, (req, res) => {\n const { file } = req.body as { file?: string };\n\n // Validazione del nome file: solo backup validi\n if (!file || typeof file !== 'string') {\n res.status(400).json({ error: 'Campo \"file\" obbligatorio' });\n return;\n }\n\n // Sicurezza: impedisce path traversal \u2014 accetta solo nomi backup validi\n const backupFilePattern = /^backup-\\d{4}-\\d{2}-\\d{2}-\\d{6}(-\\d{3})?\\.db$/;\n if (file.includes('/') || file.includes('..') || !backupFilePattern.test(file)) {\n res.status(400).json({ error: 'Nome file non valido (deve essere \"backup-YYYY-MM-DD-HHmmss[-mmm].db\")' });\n return;\n }\n\n try {\n // Verifica che il backup esista nell'elenco autorizzato\n const entries = listBackups(BACKUPS_DIR);\n const found = entries.find(e => e.metadata.filename === file);\n if (!found) {\n res.status(404).json({ error: `Backup non trovato: ${file}` });\n return;\n }\n\n restoreBackup(found.filePath, DB_PATH);\n\n logger.info('BACKUP', `Database ripristinato da: ${file}`);\n res.json({\n success: true,\n restoredFrom: file,\n timestamp: found.metadata.timestamp,\n message: 'Database ripristinato. Riavviare il worker per applicare le modifiche.',\n });\n } catch (error) {\n logger.error('BACKUP', `Ripristino backup fallito: ${file}`, {}, error as Error);\n res.status(500).json({ error: 'Ripristino backup fallito' });\n }\n });\n\n return router;\n}\n", "/**\n * Router Plugins: gestione lifecycle plugin via REST API.\n *\n * Endpoint:\n * GET /api/plugins \u2014 lista tutti i plugin con stato\n * POST /api/plugins/:name/enable \u2014 abilita un plugin registrato\n * POST /api/plugins/:name/disable \u2014 disabilita un plugin attivo\n *\n * La route opera sul PluginRegistry singleton; l'abilitazione richiede\n * un PluginContext con accesso al SDK. Il context viene costruito usando\n * il SDK gi\u00E0 inizializzato nel WorkerContext.\n */\n\nimport { Router } from 'express';\nimport { PluginRegistry, createPluginLogger } from '../plugins/PluginRegistry.js';\nimport { KiroMemorySDK } from '../../sdk/index.js';\nimport type { WorkerContext } from '../worker-context.js';\nimport { logger } from '../../utils/logger.js';\n\nexport function createPluginsRouter(ctx: WorkerContext): Router {\n const router = Router();\n const registry = PluginRegistry.getInstance();\n\n // SDK condiviso per il PluginContext (creato una sola volta per il router)\n const sdk = new KiroMemorySDK({ skipMigrations: true });\n\n // \u2500\u2500 GET /api/plugins \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n router.get('/api/plugins', (_req, res) => {\n try {\n const plugins = registry.getAll();\n res.json({ plugins, total: plugins.length });\n } catch (err) {\n logger.error('WORKER', 'Recupero lista plugin fallito', {}, err as Error);\n res.status(500).json({ error: 'Impossibile recuperare la lista plugin' });\n }\n });\n\n // \u2500\u2500 POST /api/plugins/:name/enable \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n router.post('/api/plugins/:name/enable', async (req, res) => {\n const { name } = req.params;\n\n if (!name || typeof name !== 'string' || name.trim().length === 0) {\n res.status(400).json({ error: 'Nome plugin non valido' });\n return;\n }\n\n const plugin = registry.get(name);\n if (!plugin) {\n res.status(404).json({ error: `Plugin non trovato: \"${name}\"` });\n return;\n }\n\n try {\n const pluginContext = {\n sdk,\n logger: createPluginLogger(name),\n config: {}, // Configurazione plugin (TODO: leggere da config.json in M3)\n };\n\n await registry.enable(name, pluginContext);\n\n const info = registry.getAll().find((p) => p.name === name);\n res.json({ success: true, plugin: info });\n } catch (err) {\n logger.error('WORKER', `Abilitazione plugin \"${name}\" fallita`, {}, err as Error);\n res.status(500).json({ error: `Impossibile abilitare il plugin \"${name}\"` });\n }\n });\n\n // \u2500\u2500 POST /api/plugins/:name/disable \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n router.post('/api/plugins/:name/disable', async (req, res) => {\n const { name } = req.params;\n\n if (!name || typeof name !== 'string' || name.trim().length === 0) {\n res.status(400).json({ error: 'Nome plugin non valido' });\n return;\n }\n\n const plugin = registry.get(name);\n if (!plugin) {\n res.status(404).json({ error: `Plugin non trovato: \"${name}\"` });\n return;\n }\n\n try {\n await registry.disable(name);\n\n const info = registry.getAll().find((p) => p.name === name);\n res.json({ success: true, plugin: info });\n } catch (err) {\n logger.error('WORKER', `Disabilitazione plugin \"${name}\" fallita`, {}, err as Error);\n res.status(500).json({ error: `Impossibile disabilitare il plugin \"${name}\"` });\n }\n });\n\n return router;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["exports", "isInSubnet", "isCorrect", "numberToPaddedHex", "stringToPaddedHex", "testBit", "address", "defaultBits", "number", "numberString", "binaryValue", "position", "length", "positionInString", "exports", "AddressError", "message", "parseMessage", "exports", "common", "__importStar", "constants", "address_error_1", "Address4", "_Address4", "address", "subnet", "groups", "part", "hex", "padded", "i", "h", "integer", "arpaFormAddress", "output", "n", "adjust", "bigInt", "mask", "start", "end", "options", "reversed", "segments", "exports", "exports", "exports", "spanAllZeroes", "spanAll", "spanLeadingZeroes", "simpleGroup", "s", "offset", "n", "i", "spanLeadingZeroesSimple", "group", "address", "g", "addressString", "exports", "groupPossibilities", "padGroup", "simpleRegularExpression", "possibleElisions", "v6", "__importStar", "possibilities", "group", "groups", "zeroIndexes", "i", "zeroIndex", "elision", "elidedGroups", "moreLeft", "moreRight", "left", "right", "position", "common", "__importStar", "constants4", "constants6", "helpers", "ipv4_1", "regular_expressions_1", "address_error_1", "common_1", "assert", "condition", "addCommas", "number", "r", "spanLeadingZeroes4", "n", "compact", "address", "slice", "s1", "s2", "i", "paddedHex", "octet", "unsignByte", "b", "Address6", "_Address6", "optionalGroups", "subnet", "zone", "bigInt", "hex", "groups", "url", "host", "port", "result", "address4", "mask6", "arpaFormAddress", "semicolonAmount", "parts", "insertIndex", "mask", "subnetSize", "availableBits", "subnetBits", "subnetPowers", "adjust", "scope", "start", "end", "length", "options", "characters", "reversed", "zeroCounter", "zeroes", "value", "zeroLengths", "index", "correct", "badCharacters", "badAddress", "halves", "first", "last", "remaining", "group", "binary", "infix", "prefix", "udpPort", "server4", "bitsForClient4", "client4", "flagsBase2", "coneNat", "reserved", "groupIndividual", "universalLocal", "nonce", "gateway", "addr6to4", "valueWithoutPadding", "bytes", "BYTE_MAX", "multiplier", "optionalPort", "formFunction", "form", "output", "left", "right", "classes", "substringSearch", "address6", "exports", "ipv4_1", "exports", "ipv6_1", "address_error_1", "helpers", "__importStar", "Search_exports", "__export", "getObservationsByIds", "getProjectStats", "getStaleObservations", "getTimeline", "markObservationsStale", "searchObservationsFTS", "searchObservationsFTSWithRank", "searchObservationsLIKE", "searchSummariesFiltered", "existsSync", "statSync", "escapeLikePattern", "input", "sanitizeFTS5Query", "query", "t", "db", "filters", "limit", "safeQuery", "sql", "params", "BM25_WEIGHTS", "pattern", "ids", "validIds", "id", "anchorId", "depthBefore", "depthAfter", "anchor", "anchorEpoch", "before", "self", "after", "project", "row", "discoveryTokens", "readTokens", "savings", "rows", "staleObs", "obs", "files", "f", "isStale", "filepath", "stale", "placeholders", "init_Search", "__esmMin", "redactSecrets", "text", "redacted", "pattern", "SECRET_PATTERNS", "match", "init_secrets", "__esmMin", "categorize", "input", "scores", "searchText", "allFiles", "rule", "CATEGORY_RULES", "score", "kw", "pattern", "bestCategory", "bestScore", "category", "init_categorizer", "__esmMin", "Observations_exports", "__export", "consolidateObservations", "createObservation", "deleteObservation", "getObservationsByProject", "getObservationsBySession", "isDuplicateObservation", "searchObservations", "updateLastAccessed", "escapeLikePattern", "input", "db", "contentHash", "windowMs", "threshold", "memorySessionId", "project", "type", "title", "subtitle", "text", "narrative", "facts", "concepts", "filesRead", "filesModified", "promptNumber", "discoveryTokens", "now", "safeTitle", "redactSecrets", "safeText", "safeNarrative", "autoCategory", "categorize", "result", "limit", "searchTerm", "sql", "pattern", "query", "id", "ids", "validIds", "placeholders", "options", "minGroupSize", "groups", "totalMerged", "totalRemoved", "group", "obsIds", "count", "merged", "removed", "observations", "keeper", "others", "uniqueTexts", "obs", "consolidatedText", "removeIds", "o", "removePlaceholders", "init_Observations", "__esmMin", "init_secrets", "init_categorizer", "express", "cors", "dangerouslyDisableDefaultSrc", "SHOULD_BE_QUOTED", "getDefaultDirectives", "dashify", "str", "capitalLetter", "assertDirectiveValueIsValid", "directiveName", "directiveValue", "assertDirectiveValueEntryIsValid", "directiveValueEntry", "normalizeDirectives", "options", "defaultDirectives", "useDefaults", "rawDirectives", "result", "directiveNamesSeen", "directivesExplicitlyDisabled", "rawDirectiveName", "rawDirectiveValue", "element", "defaultDirectiveName", "defaultDirectiveValue", "getHeaderValue", "req", "res", "normalizedDirectives", "newElement", "contentSecurityPolicy", "headerName", "next", "ALLOWED_POLICIES$2", "getHeaderValueFromOptions$6", "policy", "crossOriginEmbedderPolicy", "headerValue", "_req", "ALLOWED_POLICIES$1", "getHeaderValueFromOptions$5", "crossOriginOpenerPolicy", "ALLOWED_POLICIES", "getHeaderValueFromOptions$4", "crossOriginResourcePolicy", "originAgentCluster", "ALLOWED_TOKENS", "getHeaderValueFromOptions$3", "tokens", "tokensSeen", "token", "referrerPolicy", "DEFAULT_MAX_AGE", "parseMaxAge", "value", "getHeaderValueFromOptions$2", "directives", "strictTransportSecurity", "xContentTypeOptions", "xDnsPrefetchControl", "xDownloadOptions", "getHeaderValueFromOptions$1", "action", "normalizedAction", "xFrameOptions", "ALLOWED_PERMITTED_POLICIES", "getHeaderValueFromOptions", "permittedPolicies", "xPermittedCrossDomainPolicies", "xPoweredBy", "xXssProtection", "getMiddlewareFunctionsFromOptions", "strictTransportSecurityOption", "xDnsPrefetchControlOption", "xFrameOptionsOption", "xPermittedCrossDomainPoliciesOption", "helmet", "middlewareFunctions", "middlewareIndex", "internalNext", "err", "middlewareFunction", "import_ip_address", "isIPv6", "isIPv62", "Buffer", "createHash", "isIP", "ipKeyGenerator", "ip", "ipv6Subnet", "MemoryStore", "validations2", "options", "key", "client", "now", "SUPPORTED_DRAFT_VERSIONS", "getResetSeconds", "windowMs", "resetTime", "resetSeconds", "deltaSeconds", "getPartitionKey", "hash", "partitionKey", "setLegacyHeaders", "response", "info", "setDraft6Headers", "windowSeconds", "setDraft7Headers", "setDraft8Headers", "name", "header", "policy", "setRetryAfterHeader", "omitUndefinedProperties", "passedOptions", "omittedOptions", "k", "ValidationError", "code", "message", "url", "ChangeWarning", "usedStores", "singleCountKeys", "validations", "request", "hits", "store", "maybeUniquePrefix", "storeKeys", "storeKey", "keys", "prefixedKey", "limit", "draft_polli_ratelimit_headers", "onLimitReached", "version", "versionString", "validOptions", "supportedValidations", "stack", "keyGenerator", "src", "getValidations", "_enabled", "enabled", "wrappedValidations", "validation", "args", "error", "isLegacyStore", "promisifyStore", "passedStore", "legacyStore", "PromisifiedStore", "resolve", "reject", "totalHits", "getOptionsFromConfig", "config", "directlyPassableEntries", "parseOptions", "notUndefinedOptions", "standardHeaders", "_response", "duration", "property", "seconds", "minutes", "hours", "days", "_request", "subnet", "_next", "_optionsUsed", "handleAsyncErrors", "fn", "next", "rateLimit", "middleware", "augmentedRequest", "incrementResult", "decremented", "decrementKey", "getThrowFn", "rate_limit_default", "crypto", "join", "dirname", "existsSync", "mkdirSync", "writeFileSync", "unlinkSync", "chmodSync", "fileURLToPath", "BetterSqlite3", "Database", "path", "options", "sql", "params", "stmt", "cached", "BunQueryCompat", "fn", "db", "join", "dirname", "basename", "homedir", "existsSync", "mkdirSync", "fileURLToPath", "appendFileSync", "existsSync", "mkdirSync", "readFileSync", "join", "homedir", "LogLevel", "DEFAULT_DATA_DIR", "Logger", "logsDir", "date", "error", "settingsPath", "settingsData", "settings", "envLevel", "sessionId", "observationNum", "data", "keys", "year", "month", "day", "hours", "minutes", "seconds", "ms", "level", "component", "message", "context", "timestamp", "levelStr", "componentStr", "correlationStr", "dataStr", "contextStr", "memorySessionId", "correlationId", "rest", "k", "v", "logLine", "durationMs", "fallback", "callerMatch", "location", "enhancedContext", "logger", "getDirname", "dirname", "fileURLToPath", "_dirname", "_legacyDir", "join", "homedir", "_defaultDir", "existsSync", "DATA_DIR", "KIRO_CONFIG_DIR", "PLUGIN_ROOT", "ARCHIVES_DIR", "LOGS_DIR", "TRASH_DIR", "BACKUPS_DIR", "MODES_DIR", "USER_SETTINGS_PATH", "_legacyDb", "DB_PATH", "VECTOR_DB_DIR", "OBSERVER_SESSIONS_DIR", "KIRO_SETTINGS_PATH", "KIRO_CONTEXT_PATH", "ensureDir", "dirPath", "mkdirSync", "SQLITE_MMAP_SIZE_BYTES", "SQLITE_CACHE_SIZE_PAGES", "KiroMemoryDatabase", "dbPath", "DB_PATH", "skipMigrations", "ensureDir", "DATA_DIR", "Database", "MigrationRunner", "sql", "params", "fn", "db", "currentVersion", "migrations", "migration", "logger", "MODEL_CONFIGS", "FASTEMBED_COMPATIBLE_MODELS", "EmbeddingService", "envModel", "dimensions", "logger", "result", "fastembed", "EmbeddingModel", "FlagEmbedding", "error", "transformers", "pipeline", "text", "truncated", "texts", "t", "results", "embeddings", "batch", "vec", "output", "dims", "data", "offset", "embedding", "embeddingService", "getEmbeddingService", "DEFAULT_MAX_CANDIDATES", "cosineSimilarity", "a", "b", "len", "dotProduct", "normA", "normB", "i", "ai", "bi", "denominator", "float32ToBuffer", "arr", "bufferToFloat32", "buf", "arrayBuffer", "VectorSearch", "db", "queryEmbedding", "options", "limit", "threshold", "maxCandidates", "conditions", "params", "sql", "rows", "scored", "row", "embedding", "similarity", "logger", "error", "observationId", "model", "blob", "batchSize", "embeddingService", "getEmbeddingService", "count", "parts", "fullText", "totalRow", "embeddedRow", "total", "embedded", "percentage", "vectorSearch", "getVectorSearch", "SEARCH_WEIGHTS", "CONTEXT_WEIGHTS", "recencyScore", "createdAtEpoch", "halfLifeHours", "ageMs", "ageHours", "normalizeFTS5Rank", "rank", "allRanks", "minRank", "maxRank", "projectMatchScore", "itemProject", "targetProject", "computeCompositeScore", "signals", "weights", "KNOWLEDGE_TYPE_BOOST", "knowledgeTypeBoost", "type", "HybridSearch", "embeddingService", "getEmbeddingService", "logger", "error", "db", "query", "options", "limit", "weights", "SEARCH_WEIGHTS", "targetProject", "rawItems", "queryEmbedding", "vectorResults", "getVectorSearch", "hit", "searchObservationsFTSWithRank", "keywordResults", "obs", "id", "existing", "allFTS5Ranks", "item", "scored", "signals", "normalizeFTS5Rank", "recencyScore", "projectMatchScore", "score", "computeCompositeScore", "isHybrid", "finalScore", "knowledgeTypeBoost", "a", "b", "finalResults", "updateLastAccessed", "ids", "r", "hybridSearch", "getHybridSearch", "MAX_SSE_CLIENTS", "clients", "getClients", "getMaxSSEClients", "addClient", "res", "removeClient", "index", "broadcast", "event", "data", "message", "client", "err", "logger", "projectsCache", "PROJECTS_CACHE_TTL", "invalidateProjectsCache", "parseIntSafe", "value", "defaultVal", "min", "max", "parsed", "isValidProject", "project", "isValidString", "val", "maxLen", "generateEmbeddingForObservation", "db", "observationId", "title", "content", "concepts", "embeddingService", "getEmbeddingService", "parts", "fullText", "embedding", "getVectorSearch", "error", "createWorkerContext", "id", "KNOWLEDGE_TYPES", "KNOWLEDGE_TYPE_LIST", "KNOWLEDGE_TYPES", "KNOWLEDGE_PLACEHOLDERS", "toEpochThreshold", "maxAgeDays", "buildKnowledgeImportanceExemptionClause", "getRetentionStats", "db", "config", "obsThreshold", "sumThreshold", "promptThreshold", "knowledgeThreshold", "importanceExemption", "observations", "summaries", "prompts", "knowledge", "total", "countRows", "sql", "params", "applyRetention", "counts", "obsParams", "obsWhere", "kParams", "kWhere", "buildRetentionConfig", "getNum", "key", "fallback", "v", "n", "existsSync", "mkdirSync", "copyFileSync", "readdirSync", "statSync", "unlinkSync", "readFileSync", "writeFileSync", "join", "basename", "formatTimestamp", "date", "pad", "n", "len", "year", "month", "day", "hours", "mins", "secs", "ms", "collectStats", "db", "dbPath", "countTable", "table", "dbSizeBytes", "existsSync", "statSync", "getSchemaVersion", "createBackup", "backupDir", "mkdirSync", "now", "ts", "filename", "destPath", "join", "metaFilename", "metaPath", "copyFileSync", "logger", "walPath", "shmPath", "stats", "schemaVersion", "metadata", "writeFileSync", "listBackups", "entries", "files", "readdirSync", "err", "metaFiles", "f", "metaFile", "dbFilename", "filePath", "raw", "readFileSync", "a", "b", "restoreBackup", "backupFile", "walBackup", "shmBackup", "walDest", "shmDest", "unlinkSync", "rotateBackups", "maxKeep", "toDelete", "deleted", "entry", "extra", "basename", "existsSync", "statSync", "readFileSync", "writeFileSync", "mkdirSync", "join", "homedir", "getConfigPath", "dataDir", "join", "homedir", "CONFIG_DEFAULTS", "readConfig", "configPath", "path", "existsSync", "raw", "readFileSync", "parsed", "getConfigValue", "key", "configPath", "config", "readConfig", "CONFIG_DEFAULTS", "listConfig", "configPath", "config", "readConfig", "merged", "CONFIG_DEFAULTS", "k", "v", "existsSync", "readdirSync", "readFileSync", "join", "resolve", "KIRO_MEMORY_VERSION", "loadModuleFromPath", "modulePath", "PluginLoader", "registry", "options", "join", "DATA_DIR", "nodeModulesDir", "existsSync", "logger", "discovered", "entries", "readdirSync", "entry", "scopeDir", "scopedEntries", "scoped", "pkgPath", "err", "pkgDir", "pkgJsonPath", "raw", "readFileSync", "pkg", "pluginPath", "absolutePath", "resolve", "entryPoint", "rawModule", "plugin", "indexPath", "candidate", "rawPlugins", "readConfig", "nameOrPath", "msg", "displayName", "label", "typedPlugin", "nodeModulesPath", "localPath", "name", "existing", "loaded", "failed", "discoveredPaths", "pluginName", "localPlugins", "effectiveName", "result", "pluginDir", "p", "current", "required", "toNumbers", "v", "part", "cMaj", "cMin", "cPat", "rMaj", "rMin", "rPat", "TIMEOUT_INIT_MS", "TIMEOUT_DESTROY_MS", "TIMEOUT_HOOK_MS", "withTimeout", "promise", "timeoutMs", "label", "resolve", "reject", "timer", "value", "err", "PluginRegistry", "_PluginRegistry", "plugin", "logger", "name", "entry", "context", "hookName", "payload", "activeEntries", "e", "hookFn", "hookPromise", "msg", "createPluginLogger", "pluginName", "prefix", "args", "Router", "encodeCursor", "id", "epoch", "raw", "decodeCursor", "cursor", "colonIdx", "epochStr", "idStr", "buildNextCursor", "rows", "limit", "last", "createSession", "db", "contentSessionId", "project", "userPrompt", "now", "result", "getSessionByContentId", "completeSession", "db", "id", "now", "getAllSessions", "db", "limit", "getSessionsByProject", "project", "init_Observations", "escapeLikePattern", "input", "createSummary", "db", "sessionId", "project", "request", "investigated", "learned", "completed", "nextSteps", "notes", "now", "result", "getSummariesByProject", "db", "project", "limit", "searchSummaries", "searchTerm", "sql", "pattern", "escapeLikePattern", "query", "createPrompt", "db", "contentSessionId", "project", "promptNumber", "promptText", "now", "result", "getPromptsByProject", "db", "project", "limit", "createCheckpoint", "db", "sessionId", "project", "data", "now", "result", "getLatestCheckpoint", "getLatestCheckpointByProject", "getReportData", "db", "project", "startEpoch", "endEpoch", "startDate", "endDate", "days", "label", "countInRange", "table", "epochCol", "sql", "stmt", "observations", "summaries", "prompts", "sessions", "timelineSql", "timelineStmt", "timeline", "typeSql", "typeStmt", "typeDistribution", "sessionTotalSql", "sessionTotal", "sessionCompletedSql", "sessionCompleted", "sessionAvgSql", "avgRow", "avgDurationMinutes", "knowledgeSql", "knowledgeCount", "staleSql", "staleCount", "summarySql", "summaryRows", "topLearnings", "completedTasks", "nextStepsArr", "row", "parts", "filesSql", "fileRows", "fileCounts", "files", "f", "file", "fileHotspots", "count", "a", "b", "init_Search", "createGithubLink", "db", "data", "now", "result", "getGithubLinksByObservation", "observationId", "getGithubLinksByRepo", "repo", "limit", "getGithubLinksByIssue", "issueNumber", "getGithubLinksByPR", "prNumber", "searchGithubLinks", "query", "options", "event_type", "safeLimit", "conditions", "params", "pattern", "where", "listReposWithLinkCount", "createHash", "JSONL_SCHEMA_VERSION", "IMPORT_BATCH_SIZE", "countExportRecords", "db", "filters", "fromEpoch", "toEpoch", "filtersToEpoch", "obsConds", "buildConditions", "sumConds", "promptConds", "obsCount", "sumCount", "promptCount", "generateMetaRecord", "counts", "meta", "exportObservationsStreaming", "onRow", "batchSize", "conds", "offset", "total", "rows", "row", "record", "exportSummariesStreaming", "exportPromptsStreaming", "validateJsonlRow", "raw", "rec", "validTypes", "computeImportHash", "payload", "hashExistsInObservations", "hash", "importObservationBatch", "records", "dryRun", "imported", "skipped", "i", "batch", "now", "importSummaryBatch", "importPromptBatch", "importJsonl", "content", "lines", "result", "obsBuf", "sumBuf", "promptBuf", "flushBuffers", "r", "parsed", "validErr", "params", "conditions", "values", "init_Observations", "createHash", "init_Search", "KiroMemorySDK", "config", "KiroMemoryDatabase", "execSync", "getObservationsByProject", "getSummariesByProject", "getPromptsByProject", "data", "key", "val", "observationId", "title", "content", "concepts", "embeddingService", "getEmbeddingService", "parts", "fullText", "embedding", "getVectorSearch", "error", "logger", "type", "narrative", "payload", "createHash", "sessionId", "contentHash", "dedupWindow", "isDuplicateObservation", "filesRead", "filesModified", "discoveryTokens", "createObservation", "KNOWLEDGE_TYPES", "metadata", "createSummary", "query", "searchObservations", "searchSummaries", "limit", "filters", "projectFilters", "searchObservationsFTS", "searchSummariesFiltered", "ids", "getObservationsByIds", "anchorId", "depthBefore", "depthAfter", "getTimeline", "contentSessionId", "session", "getSessionByContentId", "createSession", "promptNumber", "text", "createPrompt", "completeSession", "options", "getHybridSearch", "queryEmbedding", "r", "recencyScore", "projectMatchScore", "batchSize", "tokenBudget", "summaries", "items", "observations", "knowledgeTypes", "knowledgeObs", "normalObs", "obs", "scoreObs", "signals", "baseScore", "computeCompositeScore", "CONTEXT_WEIGHTS", "knowledgeTypeBoost", "scoredKnowledge", "a", "b", "scoredNormal", "tokensUsed", "budgetItems", "item", "itemTokens", "staleObs", "getStaleObservations", "o", "markObservationsStale", "consolidateObservations", "total", "stale", "neverAccessed", "recentThreshold", "recentlyAccessed", "recentObs", "contextSnapshot", "createCheckpoint", "getLatestCheckpoint", "getLatestCheckpointByProject", "now", "startEpoch", "endEpoch", "daysBack", "getReportData", "project", "rows", "decoded", "decodeCursor", "sql", "next_cursor", "encodeCursor", "init_Search", "join", "DATA_DIR", "join", "TOKEN_FILE", "VERSION", "ALLOWED_EVENTS", "createCoreRouter", "ctx", "workerToken", "router", "Router", "notifyLimiter", "rate_limit_default", "req", "res", "event", "data", "_req", "VERSION", "clients", "getClients", "getMaxSSEClients", "addClient", "logger", "keepaliveInterval", "removeClient", "Router", "init_Observations", "init_Search", "createObservationsRouter", "ctx", "router", "Router", "req", "res", "cursor", "offset", "limit", "project", "_limit", "parseIntSafe", "rows", "decoded", "decodeCursor", "sql", "_offset", "next_cursor", "buildNextCursor", "countSql", "total", "error", "logger", "memorySessionId", "type", "title", "content", "concepts", "files", "isValidProject", "isValidString", "id", "createObservation", "ids", "observations", "getObservationsByIds", "knowledge_type", "severity", "alternatives", "reason", "metaContext", "confidence", "KNOWLEDGE_TYPES", "metadata", "obsType", "conceptStr", "context", "getObservationsByProject", "getSummariesByProject", "Router", "createSummariesRouter", "ctx", "router", "Router", "req", "res", "cursor", "offset", "limit", "project", "_limit", "parseIntSafe", "rows", "decoded", "decodeCursor", "sql", "_offset", "next_cursor", "buildNextCursor", "countSql", "total", "error", "logger", "sessionId", "request", "learned", "completed", "nextSteps", "isValidProject", "MAX_FIELD", "isValidString", "id", "createSummary", "Router", "init_Search", "createSearchRouter", "ctx", "router", "Router", "req", "res", "q", "project", "type", "limit", "filters", "parseIntSafe", "results", "searchObservationsFTS", "searchSummariesFiltered", "error", "logger", "getHybridSearch", "anchor", "depth_before", "depth_after", "anchorId", "timeline", "getTimeline", "Router", "getObservationsTimeline", "db", "project", "days", "cutoffEpoch", "sql", "stmt", "getTypeDistribution", "getSessionStats", "row", "getAnalyticsOverview", "now", "todayStart", "weekStart", "projectFilter", "params", "discoveryTokens", "readTokens", "savings", "reductionPct", "getHeatmapData", "months", "AnomalyDetector", "db", "windowSize", "threshold", "project", "sessions", "logger", "durations", "s", "obsCounts", "cmdCounts", "mean", "stdDev", "baseline", "anomalies", "session", "zScore", "cmdRatio", "values", "sum", "v", "avg", "squaredDiffs", "createAnalyticsRouter", "ctx", "router", "Router", "req", "res", "project", "isValidProject", "overview", "getAnalyticsOverview", "error", "logger", "days", "timeline", "getObservationsTimeline", "parseIntSafe", "distribution", "getTypeDistribution", "stats", "getSessionStats", "months", "getHeatmapData", "limit", "_limit", "sql", "stmt", "rows", "counts", "row", "tokens", "t", "token", "result", "a", "b", "concept", "count", "windowParam", "thresholdParam", "windowSize", "threshold", "detector", "AnomalyDetector", "anomalies", "baseline", "Router", "createSessionsRouter", "ctx", "router", "Router", "req", "res", "project", "isValidProject", "sessions", "getSessionsByProject", "getAllSessions", "error", "logger", "sessionId", "checkpoint", "getLatestCheckpoint", "getLatestCheckpointByProject", "cursor", "offset", "limit", "_limit", "parseIntSafe", "rows", "decoded", "decodeCursor", "sql", "_offset", "next_cursor", "buildNextCursor", "countSql", "total", "Router", "init_Search", "createProjectsRouter", "ctx", "router", "Router", "_req", "res", "now", "projectsCache", "PROJECTS_CACHE_TTL", "rows", "r", "error", "logger", "aliases", "row", "req", "project", "displayName", "isValidProject", "stats", "getProjectStats", "Router", "formatReportMarkdown", "data", "lines", "completionPct", "entry", "learning", "task", "step", "createDataRouter", "ctx", "workerToken", "router", "Router", "requireAuth", "req", "res", "next", "batchSize", "count", "getVectorSearch", "parseIntSafe", "error", "logger", "_req", "stats", "embeddingService", "getEmbeddingService", "config", "buildRetentionConfig", "listConfig", "getRetentionStats", "result", "applyRetention", "maxAgeDays", "dryRun", "days", "threshold", "obsCount", "sumCount", "promptCount", "deleted", "obsResult", "sumResult", "promptResult", "project", "fmt", "type", "isValidProject", "daysBack", "sql", "params", "observations", "sumSql", "sumParams", "summaries", "lines", "obs", "date", "sum", "period", "format", "now", "startEpoch", "data", "getReportData", "outputFormat", "formatReportMarkdown", "Router", "crypto", "SUPPORTED_EVENTS", "ISSUE_ACTIONS", "PR_ACTIONS", "getWebhookSecret", "ctx", "fromConfig", "validateGithubSignature", "payload", "signature", "secret", "expectedSignature", "crypto", "extractIssueRefs", "text", "refs", "regex", "match", "num", "processIssuesEvent", "repo", "action", "issue", "createGithubLink", "logger", "err", "processPullRequestEvent", "pr", "effectiveAction", "processPushEvent", "commits", "pusher", "ref", "branch", "author", "commit", "message", "commitUrl", "issueRefs", "issueNumber", "rawBodyMiddleware", "req", "_res", "next", "chunks", "chunk", "rawBody", "createWebhooksRouter", "router", "Router", "webhookLimiter", "rate_limit_default", "res", "eventType", "observation_id", "query", "limit", "parseIntSafe", "links", "obsId", "getGithubLinksByObservation", "issueNum", "getGithubLinksByIssue", "prNum", "getGithubLinksByPR", "getGithubLinksByRepo", "searchGithubLinks", "_req", "repos", "listReposWithLinkCount", "Router", "createImportExportRouter", "ctx", "router", "Router", "req", "res", "project", "type", "from", "to", "isValidProject", "filters", "dateStr", "filename", "db", "generateMetaRecord", "exportObservationsStreaming", "line", "exportSummariesStreaming", "exportPromptsStreaming", "logger", "error", "express_text_middleware", "dryRun", "content", "MAX_IMPORT_BYTES", "result", "importJsonl", "next", "contentType", "chunks", "chunk", "err", "Router", "ErrorSchema", "ObservationSchema", "SummarySchema", "SessionSchema", "PromptSchema", "paginatedResponse", "itemSchema", "errorResponse", "description", "openApiSpec", "buildSwaggerHtml", "specUrl", "openApiSpec", "createDocsRouter", "router", "Router", "_req", "res", "html", "Router", "createBackupRouter", "ctx", "workerToken", "router", "Router", "requireAuth", "req", "res", "next", "_req", "items", "listBackups", "BACKUPS_DIR", "e", "error", "logger", "maxKeep", "getConfigValue", "entry", "createBackup", "DB_PATH", "deleted", "rotateBackups", "file", "backupFilePattern", "found", "restoreBackup", "Router", "createPluginsRouter", "ctx", "router", "Router", "registry", "PluginRegistry", "sdk", "KiroMemorySDK", "_req", "res", "plugins", "err", "logger", "req", "name", "pluginContext", "createPluginLogger", "info", "p", "__worker_dirname", "dirname", "fileURLToPath", "PORT", "HOST", "PID_FILE", "join", "DATA_DIR", "TOKEN_FILE", "existsSync", "mkdirSync", "WORKER_TOKEN", "crypto", "writeFileSync", "chmodSync", "err", "logger", "db", "KiroMemoryDatabase", "getHybridSearch", "ctx", "createWorkerContext", "app", "express", "helmet", "cors", "rate_limit_default", "createCoreRouter", "createObservationsRouter", "createSummariesRouter", "createSearchRouter", "createAnalyticsRouter", "createSessionsRouter", "createProjectsRouter", "createDataRouter", "createWebhooksRouter", "createImportExportRouter", "createDocsRouter", "createBackupRouter", "createPluginsRouter", "_req", "res", "viewerPath", "scheduleRetentionCleanup", "getConfigValue", "intervalHours", "intervalMs", "runRetentionCleanup", "config", "buildRetentionConfig", "listConfig", "result", "applyRetention", "startupDelay", "retentionInterval", "scheduleBackupJob", "maxKeep", "runBackup", "entry", "createBackup", "DB_PATH", "BACKUPS_DIR", "deleted", "rotateBackups", "backupInterval", "initializePlugins", "registry", "PluginRegistry", "loaderRegistryAdapter", "p", "name", "PluginLoader", "error", "server", "shutdown", "signal", "sseClients", "getClients", "client", "forceTimeout", "unlinkSync", "reason"]
|
|
7
7
|
}
|