llm-canary 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +97 -0
  2. package/dist/__tests__/canary.test.d.ts +2 -0
  3. package/dist/__tests__/canary.test.d.ts.map +1 -0
  4. package/dist/__tests__/canary.test.js +149 -0
  5. package/dist/__tests__/canary.test.js.map +1 -0
  6. package/dist/__tests__/zero-width.test.d.ts +2 -0
  7. package/dist/__tests__/zero-width.test.d.ts.map +1 -0
  8. package/dist/__tests__/zero-width.test.js +52 -0
  9. package/dist/__tests__/zero-width.test.js.map +1 -0
  10. package/dist/canary.d.ts +6 -0
  11. package/dist/canary.d.ts.map +1 -0
  12. package/dist/canary.js +24 -0
  13. package/dist/canary.js.map +1 -0
  14. package/dist/codec.d.ts +6 -0
  15. package/dist/codec.d.ts.map +1 -0
  16. package/dist/codec.js +41 -0
  17. package/dist/codec.js.map +1 -0
  18. package/dist/detect.d.ts +3 -0
  19. package/dist/detect.d.ts.map +1 -0
  20. package/dist/detect.js +72 -0
  21. package/dist/detect.js.map +1 -0
  22. package/dist/embed.d.ts +3 -0
  23. package/dist/embed.d.ts.map +1 -0
  24. package/dist/embed.js +69 -0
  25. package/dist/embed.js.map +1 -0
  26. package/dist/encoders/homoglyph.d.ts +3 -0
  27. package/dist/encoders/homoglyph.d.ts.map +1 -0
  28. package/dist/encoders/homoglyph.js +72 -0
  29. package/dist/encoders/homoglyph.js.map +1 -0
  30. package/dist/encoders/zero-width.d.ts +3 -0
  31. package/dist/encoders/zero-width.d.ts.map +1 -0
  32. package/dist/encoders/zero-width.js +48 -0
  33. package/dist/encoders/zero-width.js.map +1 -0
  34. package/dist/generate.d.ts +3 -0
  35. package/dist/generate.d.ts.map +1 -0
  36. package/dist/generate.js +12 -0
  37. package/dist/generate.js.map +1 -0
  38. package/dist/index.d.ts +6 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +14 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/types.d.ts +45 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/types.js +3 -0
  45. package/dist/types.js.map +1 -0
  46. package/package.json +33 -0
package/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # llm-canary
2
+
3
+ Invisible canary tokens for prompt leakage detection. Embed hidden markers in prompts using zero-width Unicode characters or homoglyph substitution, then detect whether the prompt was leaked or reproduced by an untrusted party.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install llm-canary
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```typescript
14
+ import { generate, embed, detect, verify, createCanary } from 'llm-canary'
15
+
16
+ // Generate a canary token
17
+ const token = generate()
18
+ console.log(token.payload) // UUID like "3f2a1b4c-..."
19
+
20
+ // Embed into a prompt (zero-width chars appended by default)
21
+ const prompt = 'You are a helpful assistant. Answer the question below.'
22
+ const protected = embed(prompt, token)
23
+
24
+ // Later: detect if the prompt was leaked
25
+ const result = detect(receivedText)
26
+ if (result.found) {
27
+ console.log('Canary detected:', result.tokens[0].payload)
28
+ }
29
+
30
+ // Or verify a specific token
31
+ const leaked = verify(receivedText, token)
32
+ console.log('Token present:', leaked)
33
+ ```
34
+
35
+ ## createCanary() convenience API
36
+
37
+ ```typescript
38
+ const canary = createCanary({ payload: 'my-secret-id' })
39
+ const protected = canary.embed('System prompt here.')
40
+ console.log(canary.verify(protected)) // true
41
+ ```
42
+
43
+ ## Encoding types
44
+
45
+ ### zero-width (default)
46
+
47
+ Encodes the payload as a binary packet using zero-width Unicode characters appended (or prepended) to the text:
48
+
49
+ - `U+200B` = bit 0
50
+ - `U+200C` = bit 1
51
+ - `U+200D` = byte separator
52
+
53
+ The binary packet format is `[0xCA, 0x1A, length, ...payload bytes, xor_checksum]`.
54
+
55
+ Invisible to the human eye and to most rendered views; detected with high confidence when the XOR checksum is valid.
56
+
57
+ ### homoglyph
58
+
59
+ Encodes bits by substituting Latin characters with Cyrillic lookalikes (e.g. `a` → `а`, `o` → `о`). The substitution is invisible in most fonts.
60
+
61
+ ```typescript
62
+ const token = generate({ type: 'homoglyph', payload: 'my-id' })
63
+ const protected = embed(longPrompt, token) // needs enough substitutable chars
64
+ ```
65
+
66
+ The text needs sufficient substitutable characters (Latin letters in the PAIRS list). An error is thrown if capacity is insufficient.
67
+
68
+ ## embed() options
69
+
70
+ ```typescript
71
+ embed(prompt, token, { position: 'after-first-sentence' })
72
+ ```
73
+
74
+ Positions: `'start'` | `'end'` (default) | `'after-first-sentence'` | `'before-last-sentence'` | `'random'`
75
+
76
+ Only applies to `zero-width` and `whitespace` types; `homoglyph` always modifies the text in-place.
77
+
78
+ ## detect() options
79
+
80
+ ```typescript
81
+ const result = detect(text, {
82
+ types: ['zero-width'], // limit which encodings to check
83
+ minConfidence: 'high', // 'high' | 'medium' | 'low'
84
+ })
85
+ ```
86
+
87
+ ## verify()
88
+
89
+ ```typescript
90
+ import { verify } from 'llm-canary'
91
+ const present = verify(text, token) // default medium confidence
92
+ const strict = verify(text, token, { minConfidence: 'high' })
93
+ ```
94
+
95
+ ## License
96
+
97
+ MIT
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=canary.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canary.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/canary.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const generate_1 = require("../generate");
5
+ const embed_1 = require("../embed");
6
+ const detect_1 = require("../detect");
7
+ const canary_1 = require("../canary");
8
+ const ZW_SPACE = '\u200B';
9
+ const ZW_NON_JOINER = '\u200C';
10
+ const ZW_JOINER = '\u200D';
11
+ function hasZeroWidthChars(text) {
12
+ return text.includes(ZW_SPACE) || text.includes(ZW_NON_JOINER) || text.includes(ZW_JOINER);
13
+ }
14
+ (0, vitest_1.describe)('generate()', () => {
15
+ (0, vitest_1.it)('returns a token with a UUID payload by default', () => {
16
+ const token = (0, generate_1.generate)();
17
+ (0, vitest_1.expect)(token.payload).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/);
18
+ });
19
+ (0, vitest_1.it)('returns zero-width type by default', () => {
20
+ const token = (0, generate_1.generate)();
21
+ (0, vitest_1.expect)(token.type).toBe('zero-width');
22
+ });
23
+ (0, vitest_1.it)('uses custom payload when provided', () => {
24
+ const token = (0, generate_1.generate)({ payload: 'secret-id-123' });
25
+ (0, vitest_1.expect)(token.payload).toBe('secret-id-123');
26
+ });
27
+ (0, vitest_1.it)('uses custom type when provided', () => {
28
+ const token = (0, generate_1.generate)({ type: 'homoglyph' });
29
+ (0, vitest_1.expect)(token.type).toBe('homoglyph');
30
+ });
31
+ (0, vitest_1.it)('includes createdAt ISO timestamp', () => {
32
+ const token = (0, generate_1.generate)();
33
+ (0, vitest_1.expect)(() => new Date(token.createdAt)).not.toThrow();
34
+ (0, vitest_1.expect)(new Date(token.createdAt).toISOString()).toBe(token.createdAt);
35
+ });
36
+ });
37
+ (0, vitest_1.describe)('embed() zero-width', () => {
38
+ (0, vitest_1.it)('produces output containing zero-width chars', () => {
39
+ const token = (0, generate_1.generate)({ payload: 'test-payload' });
40
+ const prompt = 'Hello, world!';
41
+ const embedded = (0, embed_1.embed)(prompt, token);
42
+ (0, vitest_1.expect)(hasZeroWidthChars(embedded)).toBe(true);
43
+ });
44
+ (0, vitest_1.it)('appends to end by default', () => {
45
+ const token = (0, generate_1.generate)({ payload: 'end-test' });
46
+ const prompt = 'My prompt';
47
+ const embedded = (0, embed_1.embed)(prompt, token);
48
+ (0, vitest_1.expect)(embedded.startsWith(prompt)).toBe(true);
49
+ });
50
+ (0, vitest_1.it)('prepends when position is start', () => {
51
+ const token = (0, generate_1.generate)({ payload: 'start-test' });
52
+ const prompt = 'My prompt';
53
+ const embedded = (0, embed_1.embed)(prompt, token, { position: 'start' });
54
+ (0, vitest_1.expect)(hasZeroWidthChars(embedded.slice(0, 50))).toBe(true);
55
+ (0, vitest_1.expect)(embedded).toContain(prompt);
56
+ });
57
+ });
58
+ (0, vitest_1.describe)('detect()', () => {
59
+ (0, vitest_1.it)('finds a zero-width embedded token', () => {
60
+ const token = (0, generate_1.generate)({ payload: 'detect-me' });
61
+ const prompt = 'Some text here.';
62
+ const embedded = (0, embed_1.embed)(prompt, token);
63
+ const result = (0, detect_1.detect)(embedded);
64
+ (0, vitest_1.expect)(result.found).toBe(true);
65
+ (0, vitest_1.expect)(result.tokens).toHaveLength(1);
66
+ (0, vitest_1.expect)(result.tokens[0].type).toBe('zero-width');
67
+ (0, vitest_1.expect)(result.tokens[0].payload).toBe('detect-me');
68
+ (0, vitest_1.expect)(result.tokens[0].checksumValid).toBe(true);
69
+ (0, vitest_1.expect)(result.tokens[0].confidence).toBe('high');
70
+ });
71
+ (0, vitest_1.it)('returns found=false for plain text', () => {
72
+ const result = (0, detect_1.detect)('No hidden data here.');
73
+ (0, vitest_1.expect)(result.found).toBe(false);
74
+ (0, vitest_1.expect)(result.tokens).toHaveLength(0);
75
+ });
76
+ (0, vitest_1.it)('includes durationMs', () => {
77
+ const result = (0, detect_1.detect)('some text');
78
+ (0, vitest_1.expect)(typeof result.durationMs).toBe('number');
79
+ (0, vitest_1.expect)(result.durationMs).toBeGreaterThanOrEqual(0);
80
+ });
81
+ });
82
+ (0, vitest_1.describe)('verify() roundtrip', () => {
83
+ (0, vitest_1.it)('returns true for correctly embedded token', () => {
84
+ const token = (0, generate_1.generate)({ payload: 'verify-roundtrip' });
85
+ const embedded = (0, embed_1.embed)('The prompt text.', token);
86
+ (0, vitest_1.expect)((0, canary_1.verify)(embedded, token)).toBe(true);
87
+ });
88
+ (0, vitest_1.it)('returns false for plain text', () => {
89
+ const token = (0, generate_1.generate)({ payload: 'some-id' });
90
+ (0, vitest_1.expect)((0, canary_1.verify)('plain text with no hidden data', token)).toBe(false);
91
+ });
92
+ (0, vitest_1.it)('returns false when payload does not match', () => {
93
+ const token1 = (0, generate_1.generate)({ payload: 'token-alpha' });
94
+ const token2 = (0, generate_1.generate)({ payload: 'token-beta' });
95
+ const embedded = (0, embed_1.embed)('Hello world.', token1);
96
+ // token2 payload is different, should not be found
97
+ (0, vitest_1.expect)((0, canary_1.verify)(embedded, token2)).toBe(false);
98
+ });
99
+ });
100
+ (0, vitest_1.describe)('homoglyph roundtrip', () => {
101
+ (0, vitest_1.it)('embed then detect finds correct payload', () => {
102
+ // Packet = [0xCA, 0x1A, len, ...payload, xor] = 4+2 = 6 bytes = 48 bits needed
103
+ // Use a short payload "ab" so packet is 6 bytes = 48 bits needed
104
+ // Provide a prompt with well over 48 substitutable chars
105
+ const prompt = 'The quick brown fox jumps over the lazy dog. ' +
106
+ 'Pack my box with five dozen liquor jugs. ' +
107
+ 'How vexingly quick daft zebras jump! ' +
108
+ 'A complex mixture of every character type possible. ' +
109
+ 'Explore creative approaches to problems and overcome obstacles effectively.';
110
+ const token = (0, generate_1.generate)({ type: 'homoglyph', payload: 'ab' });
111
+ const embedded = (0, embed_1.embed)(prompt, token);
112
+ const result = (0, detect_1.detect)(embedded, { types: ['homoglyph'] });
113
+ (0, vitest_1.expect)(result.found).toBe(true);
114
+ (0, vitest_1.expect)(result.tokens[0].type).toBe('homoglyph');
115
+ (0, vitest_1.expect)(result.tokens[0].payload).toBe('ab');
116
+ (0, vitest_1.expect)(result.tokens[0].checksumValid).toBe(true);
117
+ });
118
+ (0, vitest_1.it)('throws when text has insufficient substitutable characters', () => {
119
+ const token = (0, generate_1.generate)({ type: 'homoglyph', payload: 'long-payload-that-needs-many-bits' });
120
+ (0, vitest_1.expect)(() => (0, embed_1.embed)('Hi.', token)).toThrow('Insufficient capacity');
121
+ });
122
+ });
123
+ (0, vitest_1.describe)('createCanary()', () => {
124
+ (0, vitest_1.it)('returns a canary object with token, embed, detect, verify', () => {
125
+ const canary = (0, canary_1.createCanary)();
126
+ (0, vitest_1.expect)(canary.token).toBeDefined();
127
+ (0, vitest_1.expect)(typeof canary.embed).toBe('function');
128
+ (0, vitest_1.expect)(typeof canary.detect).toBe('function');
129
+ (0, vitest_1.expect)(typeof canary.verify).toBe('function');
130
+ });
131
+ (0, vitest_1.it)('verify() returns true after embed()', () => {
132
+ const canary = (0, canary_1.createCanary)({ payload: 'canary-test' });
133
+ const embedded = canary.embed('This is the prompt text to protect.');
134
+ (0, vitest_1.expect)(canary.verify(embedded)).toBe(true);
135
+ });
136
+ (0, vitest_1.it)('respects config type homoglyph', () => {
137
+ // payload "xy" -> packet = 6 bytes = 48 bits needed
138
+ const canary = (0, canary_1.createCanary)({ type: 'homoglyph', payload: 'xy' });
139
+ (0, vitest_1.expect)(canary.token.type).toBe('homoglyph');
140
+ // Provide a long prompt with many substitutable chars
141
+ const prompt = 'Type checking is very important in every project. ' +
142
+ 'Homoglyph encoding uses lookalike characters between Latin and Cyrillic alphabets. ' +
143
+ 'Accuracy matters when processing text at a byte level. ' +
144
+ 'Every character type must be handled properly to ensure correctness.';
145
+ const embedded = canary.embed(prompt);
146
+ (0, vitest_1.expect)(canary.verify(embedded)).toBe(true);
147
+ });
148
+ });
149
+ //# sourceMappingURL=canary.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canary.test.js","sourceRoot":"","sources":["../../src/__tests__/canary.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=zero-width.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zero-width.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/zero-width.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const zero_width_1 = require("../encoders/zero-width");
5
+ const ZW_SPACE = '\u200B';
6
+ const ZW_NON_JOINER = '\u200C';
7
+ const ZW_JOINER = '\u200D';
8
+ (0, vitest_1.describe)('encodeZeroWidth', () => {
9
+ (0, vitest_1.it)('encodes bytes using only zero-width chars', () => {
10
+ const bytes = new Uint8Array([0xAB, 0xCD]);
11
+ const encoded = (0, zero_width_1.encodeZeroWidth)(bytes);
12
+ for (const ch of encoded) {
13
+ (0, vitest_1.expect)([ZW_SPACE, ZW_NON_JOINER, ZW_JOINER]).toContain(ch);
14
+ }
15
+ });
16
+ (0, vitest_1.it)('returns empty string for empty bytes', () => {
17
+ (0, vitest_1.expect)((0, zero_width_1.encodeZeroWidth)(new Uint8Array([]))).toBe('');
18
+ });
19
+ (0, vitest_1.it)('encodes a single byte as 8 zero-width bits', () => {
20
+ const bytes = new Uint8Array([0b10101010]);
21
+ const encoded = (0, zero_width_1.encodeZeroWidth)(bytes);
22
+ // No separator for single byte, 8 bit chars
23
+ const bits = [...encoded].filter(c => c !== ZW_JOINER);
24
+ (0, vitest_1.expect)(bits).toHaveLength(8);
25
+ });
26
+ });
27
+ (0, vitest_1.describe)('decodeZeroWidth roundtrip', () => {
28
+ (0, vitest_1.it)('roundtrips single byte', () => {
29
+ const bytes = new Uint8Array([42]);
30
+ const encoded = (0, zero_width_1.encodeZeroWidth)(bytes);
31
+ const decoded = (0, zero_width_1.decodeZeroWidth)(encoded);
32
+ (0, vitest_1.expect)(decoded).not.toBeNull();
33
+ (0, vitest_1.expect)(decoded).toEqual(bytes);
34
+ });
35
+ (0, vitest_1.it)('roundtrips multiple bytes', () => {
36
+ const bytes = new Uint8Array([0, 127, 255, 0xCA, 0x1A]);
37
+ const encoded = (0, zero_width_1.encodeZeroWidth)(bytes);
38
+ const decoded = (0, zero_width_1.decodeZeroWidth)(encoded);
39
+ (0, vitest_1.expect)(decoded).not.toBeNull();
40
+ (0, vitest_1.expect)(decoded).toEqual(bytes);
41
+ });
42
+ (0, vitest_1.it)('returns null when no zero-width chars present', () => {
43
+ (0, vitest_1.expect)((0, zero_width_1.decodeZeroWidth)('hello world')).toBeNull();
44
+ });
45
+ (0, vitest_1.it)('ignores surrounding normal text during roundtrip', () => {
46
+ const bytes = new Uint8Array([99, 88]);
47
+ const encoded = 'prefix' + (0, zero_width_1.encodeZeroWidth)(bytes) + 'suffix';
48
+ const decoded = (0, zero_width_1.decodeZeroWidth)(encoded);
49
+ (0, vitest_1.expect)(decoded).toEqual(bytes);
50
+ });
51
+ });
52
+ //# sourceMappingURL=zero-width.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zero-width.test.js","sourceRoot":"","sources":["../../src/__tests__/zero-width.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { Canary, CanaryConfig, CanaryToken, Confidence } from './types';
2
+ export declare function verify(text: string, token: CanaryToken, options?: {
3
+ minConfidence?: Confidence;
4
+ }): boolean;
5
+ export declare function createCanary(config?: CanaryConfig): Canary;
6
+ //# sourceMappingURL=canary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canary.d.ts","sourceRoot":"","sources":["../src/canary.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAgD,UAAU,EAAE,MAAM,SAAS,CAAA;AAE1H,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,WAAW,EAClB,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,UAAU,CAAA;CAAE,GACvC,OAAO,CAMT;AAED,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,CAS1D"}
package/dist/canary.js ADDED
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.verify = verify;
4
+ exports.createCanary = createCanary;
5
+ const generate_1 = require("./generate");
6
+ const embed_1 = require("./embed");
7
+ const detect_1 = require("./detect");
8
+ function verify(text, token, options) {
9
+ const result = (0, detect_1.detect)(text, {
10
+ types: [token.type],
11
+ minConfidence: options?.minConfidence ?? 'medium',
12
+ });
13
+ return result.tokens.some(t => t.payload === token.payload);
14
+ }
15
+ function createCanary(config) {
16
+ const token = (0, generate_1.generate)({ type: config?.type, payload: config?.payload });
17
+ return {
18
+ token,
19
+ embed: (prompt, options) => (0, embed_1.embed)(prompt, token, { position: config?.position, ...options }),
20
+ detect: (text, options) => (0, detect_1.detect)(text, options),
21
+ verify: (text) => verify(text, token),
22
+ };
23
+ }
24
+ //# sourceMappingURL=canary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canary.js","sourceRoot":"","sources":["../src/canary.ts"],"names":[],"mappings":";;AAKA,wBAUC;AAED,oCASC;AA1BD,yCAAqC;AACrC,mCAA+B;AAC/B,qCAAiC;AAGjC,SAAgB,MAAM,CACpB,IAAY,EACZ,KAAkB,EAClB,OAAwC;IAExC,MAAM,MAAM,GAAoB,IAAA,eAAM,EAAC,IAAI,EAAE;QAC3C,KAAK,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;QACnB,aAAa,EAAE,OAAO,EAAE,aAAa,IAAI,QAAQ;KAClD,CAAC,CAAA;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,CAAC,CAAA;AAC7D,CAAC;AAED,SAAgB,YAAY,CAAC,MAAqB;IAChD,MAAM,KAAK,GAAG,IAAA,mBAAQ,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;IACxE,OAAO;QACL,KAAK;QACL,KAAK,EAAE,CAAC,MAAc,EAAE,OAAsB,EAAU,EAAE,CACxD,IAAA,aAAK,EAAC,MAAM,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAC;QAClE,MAAM,EAAE,CAAC,IAAY,EAAE,OAAuB,EAAmB,EAAE,CAAC,IAAA,eAAM,EAAC,IAAI,EAAE,OAAO,CAAC;QACzF,MAAM,EAAE,CAAC,IAAY,EAAW,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC;KACvD,CAAA;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare function encodePacket(payload: string): Uint8Array;
2
+ export declare function decodePacket(bytes: Uint8Array): {
3
+ payload: string;
4
+ checksumValid: boolean;
5
+ } | null;
6
+ //# sourceMappingURL=codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"AAGA,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAcxD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAelG"}
package/dist/codec.js ADDED
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.encodePacket = encodePacket;
4
+ exports.decodePacket = decodePacket;
5
+ // Packet format: [0xCA, 0x1A, length, ...payload bytes, xor_checksum]
6
+ const MAGIC = [0xca, 0x1a];
7
+ function encodePacket(payload) {
8
+ const encoder = new TextEncoder();
9
+ const payloadBytes = encoder.encode(payload);
10
+ let checksum = 0;
11
+ for (const b of payloadBytes) {
12
+ checksum ^= b;
13
+ }
14
+ const packet = new Uint8Array(MAGIC.length + 1 + payloadBytes.length + 1);
15
+ packet[0] = MAGIC[0];
16
+ packet[1] = MAGIC[1];
17
+ packet[2] = payloadBytes.length;
18
+ packet.set(payloadBytes, 3);
19
+ packet[packet.length - 1] = checksum;
20
+ return packet;
21
+ }
22
+ function decodePacket(bytes) {
23
+ if (bytes.length < 4)
24
+ return null;
25
+ if (bytes[0] !== MAGIC[0] || bytes[1] !== MAGIC[1])
26
+ return null;
27
+ const length = bytes[2];
28
+ if (bytes.length < 3 + length + 1)
29
+ return null;
30
+ const payloadBytes = bytes.slice(3, 3 + length);
31
+ const storedChecksum = bytes[3 + length];
32
+ let computed = 0;
33
+ for (const b of payloadBytes) {
34
+ computed ^= b;
35
+ }
36
+ const checksumValid = computed === storedChecksum;
37
+ const decoder = new TextDecoder();
38
+ const payload = decoder.decode(payloadBytes);
39
+ return { payload, checksumValid };
40
+ }
41
+ //# sourceMappingURL=codec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.js","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":";;AAGA,oCAcC;AAED,oCAeC;AAlCD,sEAAsE;AACtE,MAAM,KAAK,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;AAE1B,SAAgB,YAAY,CAAC,OAAe;IAC1C,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;IACjC,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IAC5C,IAAI,QAAQ,GAAG,CAAC,CAAA;IAChB,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;QAC7B,QAAQ,IAAI,CAAC,CAAA;IACf,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IACzE,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACpB,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACpB,MAAM,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,MAAM,CAAA;IAC/B,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,CAAA;IAC3B,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAA;IACpC,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAgB,YAAY,CAAC,KAAiB;IAC5C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IACjC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IAC/D,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACvB,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IAC9C,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAA;IAC/C,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,CAAA;IACxC,IAAI,QAAQ,GAAG,CAAC,CAAA;IAChB,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;QAC7B,QAAQ,IAAI,CAAC,CAAA;IACf,CAAC;IACD,MAAM,aAAa,GAAG,QAAQ,KAAK,cAAc,CAAA;IACjD,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;IACjC,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;IAC5C,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,CAAA;AACnC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { DetectionResult, DetectOptions } from './types';
2
+ export declare function detect(text: string, options?: DetectOptions): DetectionResult;
3
+ //# sourceMappingURL=detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAiB,eAAe,EAAE,aAAa,EAA0B,MAAM,SAAS,CAAA;AAQpG,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,eAAe,CA8D7E"}
package/dist/detect.js ADDED
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detect = detect;
4
+ const zero_width_1 = require("./encoders/zero-width");
5
+ const homoglyph_1 = require("./encoders/homoglyph");
6
+ const codec_1 = require("./codec");
7
+ const CONFIDENCE_ORDER = { high: 3, medium: 2, low: 1 };
8
+ function meetsMinConfidence(conf, min) {
9
+ return CONFIDENCE_ORDER[conf] >= CONFIDENCE_ORDER[min];
10
+ }
11
+ function detect(text, options) {
12
+ const start = Date.now();
13
+ const enabledTypes = options?.types ?? ['zero-width', 'homoglyph', 'whitespace'];
14
+ const minConfidence = options?.minConfidence ?? 'low';
15
+ const tokens = [];
16
+ if (enabledTypes.includes('zero-width')) {
17
+ const bytes = (0, zero_width_1.decodeZeroWidth)(text);
18
+ if (bytes) {
19
+ const decoded = (0, codec_1.decodePacket)(bytes);
20
+ if (decoded) {
21
+ const confidence = decoded.checksumValid ? 'high' : 'medium';
22
+ if (meetsMinConfidence(confidence, minConfidence)) {
23
+ // Find position of zero-width chars in text
24
+ const startIdx = text.search(/[\u200B\u200C\u200D]/);
25
+ let endIdx = startIdx;
26
+ if (startIdx !== -1) {
27
+ for (let i = startIdx; i < text.length; i++) {
28
+ const c = text[i];
29
+ if (c === '\u200B' || c === '\u200C' || c === '\u200D') {
30
+ endIdx = i + 1;
31
+ }
32
+ else if (endIdx > startIdx) {
33
+ break;
34
+ }
35
+ }
36
+ }
37
+ tokens.push({
38
+ type: 'zero-width',
39
+ payload: decoded.payload,
40
+ confidence,
41
+ checksumValid: decoded.checksumValid,
42
+ position: { start: startIdx === -1 ? 0 : startIdx, end: endIdx },
43
+ });
44
+ }
45
+ }
46
+ }
47
+ }
48
+ if (enabledTypes.includes('homoglyph')) {
49
+ const bytes = (0, homoglyph_1.decodeHomoglyph)(text);
50
+ if (bytes) {
51
+ const decoded = (0, codec_1.decodePacket)(bytes);
52
+ if (decoded) {
53
+ const confidence = decoded.checksumValid ? 'high' : 'medium';
54
+ if (meetsMinConfidence(confidence, minConfidence)) {
55
+ tokens.push({
56
+ type: 'homoglyph',
57
+ payload: decoded.payload,
58
+ confidence,
59
+ checksumValid: decoded.checksumValid,
60
+ position: { start: 0, end: text.length },
61
+ });
62
+ }
63
+ }
64
+ }
65
+ }
66
+ return {
67
+ found: tokens.length > 0,
68
+ tokens,
69
+ durationMs: Date.now() - start,
70
+ };
71
+ }
72
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { CanaryToken, EmbedOptions } from './types';
2
+ export declare function embed(prompt: string, token: CanaryToken, options?: EmbedOptions): string;
3
+ //# sourceMappingURL=embed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embed.d.ts","sourceRoot":"","sources":["../src/embed.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAmCxD,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,CA+BxF"}
package/dist/embed.js ADDED
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.embed = embed;
4
+ const zero_width_1 = require("./encoders/zero-width");
5
+ const homoglyph_1 = require("./encoders/homoglyph");
6
+ const codec_1 = require("./codec");
7
+ function insertAt(prompt, marker, position) {
8
+ const pos = position ?? 'end';
9
+ if (pos === 'start') {
10
+ return marker + prompt;
11
+ }
12
+ if (pos === 'end') {
13
+ return prompt + marker;
14
+ }
15
+ if (pos === 'after-first-sentence') {
16
+ const match = prompt.match(/[.!?]\s/);
17
+ if (match && match.index !== undefined) {
18
+ const idx = match.index + 1;
19
+ return prompt.slice(0, idx) + marker + prompt.slice(idx);
20
+ }
21
+ return prompt + marker;
22
+ }
23
+ if (pos === 'before-last-sentence') {
24
+ // Find start of last sentence
25
+ const trimmed = prompt.trimEnd();
26
+ const match = [...trimmed.matchAll(/[.!?]\s+/g)].at(-1);
27
+ if (match && match.index !== undefined) {
28
+ const idx = match.index + match[0].length;
29
+ return prompt.slice(0, idx) + marker + prompt.slice(idx);
30
+ }
31
+ return marker + prompt;
32
+ }
33
+ if (pos === 'random') {
34
+ const idx = Math.floor(Math.random() * (prompt.length + 1));
35
+ return prompt.slice(0, idx) + marker + prompt.slice(idx);
36
+ }
37
+ return prompt + marker;
38
+ }
39
+ function embed(prompt, token, options) {
40
+ const bytes = (0, codec_1.encodePacket)(token.payload);
41
+ if (token.type === 'homoglyph') {
42
+ // encodeHomoglyph replaces chars in-place, returns full modified text
43
+ return (0, homoglyph_1.encodeHomoglyph)(prompt, bytes);
44
+ }
45
+ if (token.type === 'whitespace') {
46
+ // Encode as trailing spaces per line: 1 space = bit 0, 2 spaces = bit 1
47
+ // Each line carries one bit; we need bytes.length * 8 lines
48
+ const bits = [];
49
+ for (const byte of bytes) {
50
+ for (let bit = 7; bit >= 0; bit--) {
51
+ bits.push((byte >> bit) & 1);
52
+ }
53
+ }
54
+ const lines = prompt.split('\n');
55
+ let bitIndex = 0;
56
+ const encodedLines = lines.map(line => {
57
+ if (bitIndex >= bits.length)
58
+ return line;
59
+ const trailing = bits[bitIndex] === 1 ? ' ' : ' ';
60
+ bitIndex++;
61
+ return line + trailing;
62
+ });
63
+ return encodedLines.join('\n');
64
+ }
65
+ // zero-width (default)
66
+ const marker = (0, zero_width_1.encodeZeroWidth)(bytes);
67
+ return insertAt(prompt, marker, options?.position);
68
+ }
69
+ //# sourceMappingURL=embed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embed.js","sourceRoot":"","sources":["../src/embed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function encodeHomoglyph(text: string, bytes: Uint8Array): string;
2
+ export declare function decodeHomoglyph(text: string): Uint8Array | null;
3
+ //# sourceMappingURL=homoglyph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"homoglyph.d.ts","sourceRoot":"","sources":["../../src/encoders/homoglyph.ts"],"names":[],"mappings":"AAgBA,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CA8BvE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAwB/D"}
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ // Latin <-> Cyrillic lookalike substitution
3
+ // bit 0 = keep Latin, bit 1 = substitute with Cyrillic
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.encodeHomoglyph = encodeHomoglyph;
6
+ exports.decodeHomoglyph = decodeHomoglyph;
7
+ const PAIRS = [
8
+ ['a', 'а'], ['c', 'с'], ['e', 'е'], ['o', 'о'],
9
+ ['p', 'р'], ['x', 'х'], ['y', 'у'],
10
+ ['A', 'А'], ['B', 'В'], ['C', 'С'], ['E', 'Е'],
11
+ ['H', 'Н'], ['K', 'К'], ['M', 'М'], ['O', 'О'],
12
+ ['P', 'Р'], ['T', 'Т'], ['X', 'Х'], ['Y', 'У'],
13
+ ];
14
+ // Build lookup maps
15
+ const latinToCyrillic = new Map(PAIRS.map(([l, c]) => [l, c]));
16
+ const cyrillicToLatin = new Map(PAIRS.map(([l, c]) => [c, l]));
17
+ const substitutable = new Set(PAIRS.map(([l]) => l));
18
+ function encodeHomoglyph(text, bytes) {
19
+ // Find all substitutable positions in text
20
+ const positions = [];
21
+ for (let i = 0; i < text.length; i++) {
22
+ if (substitutable.has(text[i])) {
23
+ positions.push(i);
24
+ }
25
+ }
26
+ // We need one substitutable position per bit
27
+ const bitsNeeded = bytes.length * 8;
28
+ if (positions.length < bitsNeeded) {
29
+ throw new Error(`Insufficient capacity: need ${bitsNeeded} substitutable chars, found ${positions.length}`);
30
+ }
31
+ const chars = text.split('');
32
+ let bitIndex = 0;
33
+ for (let byteIndex = 0; byteIndex < bytes.length; byteIndex++) {
34
+ for (let bit = 7; bit >= 0; bit--) {
35
+ const bitVal = (bytes[byteIndex] >> bit) & 1;
36
+ const pos = positions[bitIndex];
37
+ if (bitVal === 1) {
38
+ chars[pos] = latinToCyrillic.get(chars[pos]) ?? chars[pos];
39
+ }
40
+ bitIndex++;
41
+ }
42
+ }
43
+ return chars.join('');
44
+ }
45
+ function decodeHomoglyph(text) {
46
+ // Collect positions that are either Cyrillic lookalikes (=1) or Latin substitutables (=0)
47
+ const bits = [];
48
+ for (const ch of text) {
49
+ if (cyrillicToLatin.has(ch)) {
50
+ bits.push(1);
51
+ }
52
+ else if (substitutable.has(ch)) {
53
+ bits.push(0);
54
+ }
55
+ }
56
+ if (bits.length === 0)
57
+ return null;
58
+ // Reconstruct bytes from bits (groups of 8)
59
+ const byteCount = Math.floor(bits.length / 8);
60
+ if (byteCount === 0)
61
+ return null;
62
+ const bytes = new Uint8Array(byteCount);
63
+ for (let i = 0; i < byteCount; i++) {
64
+ let byte = 0;
65
+ for (let bit = 0; bit < 8; bit++) {
66
+ byte = (byte << 1) | bits[i * 8 + bit];
67
+ }
68
+ bytes[i] = byte;
69
+ }
70
+ return bytes;
71
+ }
72
+ //# sourceMappingURL=homoglyph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"homoglyph.js","sourceRoot":"","sources":["../../src/encoders/homoglyph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function encodeZeroWidth(bytes: Uint8Array): string;
2
+ export declare function decodeZeroWidth(text: string): Uint8Array | null;
3
+ //# sourceMappingURL=zero-width.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zero-width.d.ts","sourceRoot":"","sources":["../../src/encoders/zero-width.ts"],"names":[],"mappings":"AASA,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAWzD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAmB/D"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ // Encode binary data as zero-width Unicode characters:
3
+ // bit 0 = U+200B (zero-width space)
4
+ // bit 1 = U+200C (zero-width non-joiner)
5
+ // byte separator = U+200D (zero-width joiner)
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.encodeZeroWidth = encodeZeroWidth;
8
+ exports.decodeZeroWidth = decodeZeroWidth;
9
+ const BIT0 = '\u200B';
10
+ const BIT1 = '\u200C';
11
+ const SEP = '\u200D';
12
+ function encodeZeroWidth(bytes) {
13
+ if (bytes.length === 0)
14
+ return '';
15
+ const parts = [];
16
+ for (const byte of bytes) {
17
+ let byteStr = '';
18
+ for (let bit = 7; bit >= 0; bit--) {
19
+ byteStr += (byte >> bit) & 1 ? BIT1 : BIT0;
20
+ }
21
+ parts.push(byteStr);
22
+ }
23
+ return parts.join(SEP);
24
+ }
25
+ function decodeZeroWidth(text) {
26
+ // Extract all zero-width chars
27
+ const zwChars = [...text].filter(c => c === BIT0 || c === BIT1 || c === SEP);
28
+ if (zwChars.length === 0)
29
+ return null;
30
+ // Split on SEP to get per-byte bit strings
31
+ const segments = zwChars.join('').split(SEP);
32
+ const bytes = [];
33
+ for (const seg of segments) {
34
+ if (seg.length === 0)
35
+ continue;
36
+ if (seg.length !== 8)
37
+ continue;
38
+ let byte = 0;
39
+ for (let i = 0; i < 8; i++) {
40
+ byte = (byte << 1) | (seg[i] === BIT1 ? 1 : 0);
41
+ }
42
+ bytes.push(byte);
43
+ }
44
+ if (bytes.length === 0)
45
+ return null;
46
+ return new Uint8Array(bytes);
47
+ }
48
+ //# sourceMappingURL=zero-width.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zero-width.js","sourceRoot":"","sources":["../../src/encoders/zero-width.ts"],"names":[],"mappings":";AAAA,uDAAuD;AACvD,oCAAoC;AACpC,yCAAyC;AACzC,8CAA8C;;AAM9C,0CAWC;AAED,0CAmBC;AApCD,MAAM,IAAI,GAAG,QAAQ,CAAA;AACrB,MAAM,IAAI,GAAG,QAAQ,CAAA;AACrB,MAAM,GAAG,GAAG,QAAQ,CAAA;AAEpB,SAAgB,eAAe,CAAC,KAAiB;IAC/C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IACjC,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,OAAO,GAAG,EAAE,CAAA;QAChB,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;QAC5C,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACrB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC;AAED,SAAgB,eAAe,CAAC,IAAY;IAC1C,+BAA+B;IAC/B,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,CAAA;IAC5E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAErC,2CAA2C;IAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC5C,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;YAAE,SAAQ;QAC9B,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;YAAE,SAAQ;QAC9B,IAAI,IAAI,GAAG,CAAC,CAAA;QACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAChD,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAClB,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACnC,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,CAAA;AAC9B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CanaryToken, GenerateOptions } from './types';
2
+ export declare function generate(options?: GenerateOptions): CanaryToken;
3
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE3D,wBAAgB,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,WAAW,CAM/D"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generate = generate;
4
+ const crypto_1 = require("crypto");
5
+ function generate(options) {
6
+ return {
7
+ type: options?.type ?? 'zero-width',
8
+ payload: options?.payload ?? (0, crypto_1.randomUUID)(),
9
+ createdAt: new Date().toISOString(),
10
+ };
11
+ }
12
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":";;AAGA,4BAMC;AATD,mCAAmC;AAGnC,SAAgB,QAAQ,CAAC,OAAyB;IAChD,OAAO;QACL,IAAI,EAAE,OAAO,EAAE,IAAI,IAAI,YAAY;QACnC,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,IAAA,mBAAU,GAAE;QACzC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAA;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { generate } from './generate';
2
+ export { embed } from './embed';
3
+ export { detect } from './detect';
4
+ export { verify, createCanary } from './canary';
5
+ export type { CanaryType, Confidence, CanaryToken, GenerateOptions, EmbedOptions, DetectedToken, DetectionResult, DetectOptions, CanaryConfig, Canary, } from './types';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAE/C,YAAY,EACV,UAAU,EACV,UAAU,EACV,WAAW,EACX,eAAe,EACf,YAAY,EACZ,aAAa,EACb,eAAe,EACf,aAAa,EACb,YAAY,EACZ,MAAM,GACP,MAAM,SAAS,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCanary = exports.verify = exports.detect = exports.embed = exports.generate = void 0;
4
+ // llm-canary - Invisible canary tokens for prompt leakage detection
5
+ var generate_1 = require("./generate");
6
+ Object.defineProperty(exports, "generate", { enumerable: true, get: function () { return generate_1.generate; } });
7
+ var embed_1 = require("./embed");
8
+ Object.defineProperty(exports, "embed", { enumerable: true, get: function () { return embed_1.embed; } });
9
+ var detect_1 = require("./detect");
10
+ Object.defineProperty(exports, "detect", { enumerable: true, get: function () { return detect_1.detect; } });
11
+ var canary_1 = require("./canary");
12
+ Object.defineProperty(exports, "verify", { enumerable: true, get: function () { return canary_1.verify; } });
13
+ Object.defineProperty(exports, "createCanary", { enumerable: true, get: function () { return canary_1.createCanary; } });
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,oEAAoE;AACpE,uCAAqC;AAA5B,oGAAA,QAAQ,OAAA;AACjB,iCAA+B;AAAtB,8FAAA,KAAK,OAAA;AACd,mCAAiC;AAAxB,gGAAA,MAAM,OAAA;AACf,mCAA+C;AAAtC,gGAAA,MAAM,OAAA;AAAE,sGAAA,YAAY,OAAA"}
@@ -0,0 +1,45 @@
1
+ export type CanaryType = 'zero-width' | 'homoglyph' | 'whitespace' | 'custom';
2
+ export type Confidence = 'high' | 'medium' | 'low';
3
+ export interface CanaryToken {
4
+ type: CanaryType;
5
+ payload: string;
6
+ createdAt: string;
7
+ }
8
+ export interface GenerateOptions {
9
+ payload?: string;
10
+ type?: CanaryType;
11
+ }
12
+ export interface EmbedOptions {
13
+ position?: 'start' | 'end' | 'after-first-sentence' | 'before-last-sentence' | 'random';
14
+ }
15
+ export interface DetectedToken {
16
+ type: CanaryType;
17
+ payload: string;
18
+ confidence: Confidence;
19
+ checksumValid: boolean;
20
+ position: {
21
+ start: number;
22
+ end: number;
23
+ };
24
+ }
25
+ export interface DetectionResult {
26
+ found: boolean;
27
+ tokens: DetectedToken[];
28
+ durationMs: number;
29
+ }
30
+ export interface DetectOptions {
31
+ types?: CanaryType[];
32
+ minConfidence?: Confidence;
33
+ }
34
+ export interface CanaryConfig {
35
+ type?: CanaryType;
36
+ payload?: string;
37
+ position?: EmbedOptions['position'];
38
+ }
39
+ export interface Canary {
40
+ token: CanaryToken;
41
+ embed(prompt: string, options?: EmbedOptions): string;
42
+ detect(text: string, options?: DetectOptions): DetectionResult;
43
+ verify(text: string): boolean;
44
+ }
45
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,WAAW,GAAG,YAAY,GAAG,QAAQ,CAAA;AAC7E,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAA;AAElD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,UAAU,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,UAAU,CAAA;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,sBAAsB,GAAG,sBAAsB,GAAG,QAAQ,CAAA;CACxF;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,UAAU,CAAA;IACtB,aAAa,EAAE,OAAO,CAAA;IACtB,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,UAAU,EAAE,CAAA;IACpB,aAAa,CAAC,EAAE,UAAU,CAAA;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,YAAY,CAAC,UAAU,CAAC,CAAA;CACpC;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,WAAW,CAAA;IAClB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,CAAA;IACrD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,eAAe,CAAA;IAC9D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;CAC9B"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "llm-canary",
3
+ "version": "0.1.0",
4
+ "description": "Invisible canary tokens for prompt leakage detection",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "test": "vitest run",
13
+ "lint": "eslint src/",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [],
17
+ "author": "",
18
+ "license": "MIT",
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^25.5.0",
27
+ "@typescript-eslint/eslint-plugin": "^8.57.1",
28
+ "@typescript-eslint/parser": "^8.57.1",
29
+ "eslint": "^8.57.1",
30
+ "typescript": "^5.9.3",
31
+ "vitest": "^4.1.0"
32
+ }
33
+ }