@tjamescouch/agentchat 0.36.2 → 0.36.4

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 (84) hide show
  1. package/README.md +20 -15
  2. package/dist/bin/agentchat.js +2 -0
  3. package/dist/bin/agentchat.js.map +1 -1
  4. package/dist/lib/captcha.d.ts +36 -0
  5. package/dist/lib/captcha.d.ts.map +1 -0
  6. package/dist/lib/captcha.js +215 -0
  7. package/dist/lib/captcha.js.map +1 -0
  8. package/dist/lib/client.d.ts +8 -0
  9. package/dist/lib/client.d.ts.map +1 -1
  10. package/dist/lib/client.js +118 -0
  11. package/dist/lib/client.js.map +1 -1
  12. package/dist/lib/daemon.d.ts +2 -0
  13. package/dist/lib/daemon.d.ts.map +1 -1
  14. package/dist/lib/daemon.js +6 -0
  15. package/dist/lib/daemon.js.map +1 -1
  16. package/dist/lib/env-doctor.d.ts +50 -0
  17. package/dist/lib/env-doctor.d.ts.map +1 -0
  18. package/dist/lib/env-doctor.js +209 -0
  19. package/dist/lib/env-doctor.js.map +1 -0
  20. package/dist/lib/hnsw.d.ts +91 -0
  21. package/dist/lib/hnsw.d.ts.map +1 -0
  22. package/dist/lib/hnsw.js +486 -0
  23. package/dist/lib/hnsw.js.map +1 -0
  24. package/dist/lib/proposals.d.ts +2 -0
  25. package/dist/lib/proposals.d.ts.map +1 -1
  26. package/dist/lib/proposals.js +1 -0
  27. package/dist/lib/proposals.js.map +1 -1
  28. package/dist/lib/protocol.d.ts +14 -0
  29. package/dist/lib/protocol.d.ts.map +1 -1
  30. package/dist/lib/protocol.js +37 -0
  31. package/dist/lib/protocol.js.map +1 -1
  32. package/dist/lib/reputation.d.ts +34 -0
  33. package/dist/lib/reputation.d.ts.map +1 -1
  34. package/dist/lib/reputation.js +128 -0
  35. package/dist/lib/reputation.js.map +1 -1
  36. package/dist/lib/server/handlers/admin.d.ts +10 -7
  37. package/dist/lib/server/handlers/admin.d.ts.map +1 -1
  38. package/dist/lib/server/handlers/admin.js +73 -0
  39. package/dist/lib/server/handlers/admin.js.map +1 -1
  40. package/dist/lib/server/handlers/ban.d.ts +1 -7
  41. package/dist/lib/server/handlers/ban.d.ts.map +1 -1
  42. package/dist/lib/server/handlers/ban.js.map +1 -1
  43. package/dist/lib/server/handlers/captcha.d.ts +49 -0
  44. package/dist/lib/server/handlers/captcha.d.ts.map +1 -0
  45. package/dist/lib/server/handlers/captcha.js +203 -0
  46. package/dist/lib/server/handlers/captcha.js.map +1 -0
  47. package/dist/lib/server/handlers/disputes.d.ts +1 -7
  48. package/dist/lib/server/handlers/disputes.d.ts.map +1 -1
  49. package/dist/lib/server/handlers/disputes.js.map +1 -1
  50. package/dist/lib/server/handlers/identity.d.ts +1 -7
  51. package/dist/lib/server/handlers/identity.d.ts.map +1 -1
  52. package/dist/lib/server/handlers/identity.js +67 -11
  53. package/dist/lib/server/handlers/identity.js.map +1 -1
  54. package/dist/lib/server/handlers/index.d.ts +1 -0
  55. package/dist/lib/server/handlers/index.d.ts.map +1 -1
  56. package/dist/lib/server/handlers/index.js +2 -0
  57. package/dist/lib/server/handlers/index.js.map +1 -1
  58. package/dist/lib/server/handlers/message.d.ts +1 -7
  59. package/dist/lib/server/handlers/message.d.ts.map +1 -1
  60. package/dist/lib/server/handlers/message.js +9 -2
  61. package/dist/lib/server/handlers/message.js.map +1 -1
  62. package/dist/lib/server/handlers/nick.d.ts +1 -8
  63. package/dist/lib/server/handlers/nick.d.ts.map +1 -1
  64. package/dist/lib/server/handlers/nick.js.map +1 -1
  65. package/dist/lib/server/handlers/presence.d.ts +1 -7
  66. package/dist/lib/server/handlers/presence.d.ts.map +1 -1
  67. package/dist/lib/server/handlers/presence.js.map +1 -1
  68. package/dist/lib/server/handlers/proposal.d.ts +1 -7
  69. package/dist/lib/server/handlers/proposal.d.ts.map +1 -1
  70. package/dist/lib/server/handlers/proposal.js +1 -0
  71. package/dist/lib/server/handlers/proposal.js.map +1 -1
  72. package/dist/lib/server/handlers/skills.d.ts +1 -7
  73. package/dist/lib/server/handlers/skills.d.ts.map +1 -1
  74. package/dist/lib/server/handlers/skills.js +36 -11
  75. package/dist/lib/server/handlers/skills.js.map +1 -1
  76. package/dist/lib/server.d.ts +35 -1
  77. package/dist/lib/server.d.ts.map +1 -1
  78. package/dist/lib/server.js +152 -13
  79. package/dist/lib/server.js.map +1 -1
  80. package/dist/lib/types.d.ts +34 -4
  81. package/dist/lib/types.d.ts.map +1 -1
  82. package/dist/lib/types.js +8 -0
  83. package/dist/lib/types.js.map +1 -1
  84. package/package.json +2 -1
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Environment Doctor — startup health check for agentchat agents
3
+ *
4
+ * Scans for:
5
+ * 1. Exposed secrets in environment variables (API keys, tokens, passwords)
6
+ * 2. Missing admin public key (insecure mode — no directive verification)
7
+ * 3. Identity file permission issues
8
+ *
9
+ * Warn-only by default. Does not block startup.
10
+ */
11
+ import fs from 'fs';
12
+ // Patterns that suggest a secret value in env var names
13
+ const SECRET_NAME_PATTERNS = [
14
+ /KEY/i,
15
+ /TOKEN/i,
16
+ /SECRET/i,
17
+ /PASSWORD/i,
18
+ /CREDENTIAL/i,
19
+ /AUTH/i,
20
+ /PRIVATE/i,
21
+ ];
22
+ // Env vars that are known-safe despite matching patterns
23
+ const SAFE_ENV_NAMES = new Set([
24
+ 'COLORTERM',
25
+ 'GPG_AGENT_INFO',
26
+ 'SSH_AUTH_SOCK',
27
+ 'TERM_SESSION_ID',
28
+ 'KEYCHAIN_PATH',
29
+ 'AUTH_TYPE', // often just "none" or "basic"
30
+ 'TOKEN_ENDPOINT', // URL, not a token
31
+ 'PUBLIC_KEY', // public keys aren't secrets
32
+ 'ADMIN_PUBKEY', // this is what we want to be set
33
+ ]);
34
+ // Prefixes for values that look like real secrets
35
+ const SECRET_VALUE_PREFIXES = [
36
+ 'sk-', // OpenAI, Anthropic
37
+ 'sk-ant-', // Anthropic
38
+ 'ghp_', // GitHub PAT
39
+ 'gho_', // GitHub OAuth
40
+ 'ghs_', // GitHub App
41
+ 'ghu_', // GitHub user-to-server
42
+ 'github_pat_', // GitHub fine-grained PAT
43
+ 'xoxb-', // Slack bot
44
+ 'xoxp-', // Slack user
45
+ 'xoxs-', // Slack legacy
46
+ 'Bearer ', // Bearer tokens
47
+ 'Basic ', // Basic auth
48
+ ];
49
+ /**
50
+ * Redact a secret value for safe display
51
+ * Shows first 6 chars + asterisks, or just asterisks if too short
52
+ */
53
+ function redactValue(value) {
54
+ if (value.length <= 8) {
55
+ return '***';
56
+ }
57
+ return value.substring(0, 6) + '***';
58
+ }
59
+ /**
60
+ * Check if an env var name looks like it holds a secret
61
+ */
62
+ function looksLikeSecretName(name) {
63
+ if (SAFE_ENV_NAMES.has(name))
64
+ return false;
65
+ return SECRET_NAME_PATTERNS.some(pattern => pattern.test(name));
66
+ }
67
+ /**
68
+ * Check if a value looks like an actual secret (not just "true" or a path)
69
+ */
70
+ function looksLikeSecretValue(value) {
71
+ // Short values are probably flags, not secrets
72
+ if (value.length < 10)
73
+ return false;
74
+ // Check for known secret prefixes
75
+ if (SECRET_VALUE_PREFIXES.some(prefix => value.startsWith(prefix))) {
76
+ return true;
77
+ }
78
+ // High entropy strings (base64-ish, hex-ish) longer than 20 chars
79
+ if (value.length >= 20) {
80
+ const alphanumCount = (value.match(/[a-zA-Z0-9]/g) || []).length;
81
+ const ratio = alphanumCount / value.length;
82
+ // Mostly alphanumeric + some special chars = likely a key/token
83
+ if (ratio > 0.8)
84
+ return true;
85
+ }
86
+ return false;
87
+ }
88
+ /**
89
+ * Scan environment variables for exposed secrets
90
+ */
91
+ export function scanEnvSecrets() {
92
+ const warnings = [];
93
+ for (const [name, value] of Object.entries(process.env)) {
94
+ if (!value)
95
+ continue;
96
+ if (looksLikeSecretName(name) && looksLikeSecretValue(value)) {
97
+ warnings.push({
98
+ name,
99
+ redacted: redactValue(value),
100
+ reason: 'Potential secret exposed in environment',
101
+ });
102
+ }
103
+ }
104
+ return warnings;
105
+ }
106
+ /**
107
+ * Check if admin public key is configured
108
+ * Looks for ADMIN_PUBKEY env var or admin_pubkey in skill file
109
+ */
110
+ export function checkAdminPubkey(skillFilePath) {
111
+ // Check env var first
112
+ if (process.env.ADMIN_PUBKEY) {
113
+ return true;
114
+ }
115
+ // Check skill file if path provided
116
+ if (skillFilePath) {
117
+ try {
118
+ const content = fs.readFileSync(skillFilePath, 'utf-8');
119
+ if (content.includes('admin_pubkey')) {
120
+ return true;
121
+ }
122
+ }
123
+ catch {
124
+ // File doesn't exist or unreadable — no admin key
125
+ }
126
+ }
127
+ return false;
128
+ }
129
+ /**
130
+ * Check identity file permissions (should be 0600 — owner read/write only)
131
+ */
132
+ export function checkIdentityPermissions(identityPath) {
133
+ const issues = [];
134
+ if (!identityPath)
135
+ return issues;
136
+ try {
137
+ const stat = fs.statSync(identityPath);
138
+ const mode = stat.mode & 0o777;
139
+ if (mode & 0o077) {
140
+ issues.push(`Identity file ${identityPath} has permissions ${mode.toString(8)} — ` +
141
+ `should be 600 (owner read/write only). Other users can read your private key.`);
142
+ }
143
+ }
144
+ catch {
145
+ // File doesn't exist yet — not an issue
146
+ }
147
+ return issues;
148
+ }
149
+ /**
150
+ * Run all environment health checks
151
+ */
152
+ export function runEnvDoctor(options) {
153
+ const envWarnings = scanEnvSecrets();
154
+ const hasAdminKey = checkAdminPubkey(options?.skillFilePath);
155
+ const identityIssues = checkIdentityPermissions(options?.identityPath);
156
+ return {
157
+ warnings: envWarnings,
158
+ insecureMode: !hasAdminKey,
159
+ identityIssues,
160
+ clean: envWarnings.length === 0 && hasAdminKey && identityIssues.length === 0,
161
+ };
162
+ }
163
+ /**
164
+ * Print the envDoctor report to stderr
165
+ * Returns true if any warnings were printed
166
+ */
167
+ export function printEnvDoctorReport(options) {
168
+ const result = runEnvDoctor(options);
169
+ if (result.clean)
170
+ return false;
171
+ const lines = [];
172
+ lines.push('');
173
+ lines.push('='.repeat(60));
174
+ lines.push(' ENVIRONMENT HEALTH CHECK');
175
+ lines.push('='.repeat(60));
176
+ // Insecure mode warning
177
+ if (result.insecureMode) {
178
+ lines.push('');
179
+ lines.push('\u26A0\uFE0F WARNING: No admin public key configured.');
180
+ lines.push('\u26A0\uFE0F Running in INSECURE mode — any user can issue');
181
+ lines.push('\u26A0\uFE0F privileged commands without verification.');
182
+ lines.push('\u26A0\uFE0F Set ADMIN_PUBKEY or add admin_pubkey to your skill file.');
183
+ }
184
+ // Exposed secrets
185
+ if (result.warnings.length > 0) {
186
+ lines.push('');
187
+ lines.push('\u26A0\uFE0F Potential secrets found in environment:');
188
+ for (const w of result.warnings) {
189
+ lines.push(` ${w.name} = ${w.redacted}`);
190
+ }
191
+ lines.push('');
192
+ lines.push(' These should be managed by agentauth proxy,');
193
+ lines.push(' not passed directly as env vars.');
194
+ }
195
+ // Identity file permissions
196
+ if (result.identityIssues.length > 0) {
197
+ lines.push('');
198
+ lines.push('\u26A0\uFE0F Identity file issues:');
199
+ for (const issue of result.identityIssues) {
200
+ lines.push(` ${issue}`);
201
+ }
202
+ }
203
+ lines.push('');
204
+ lines.push('='.repeat(60));
205
+ lines.push('');
206
+ console.error(lines.join('\n'));
207
+ return true;
208
+ }
209
+ //# sourceMappingURL=env-doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-doctor.js","sourceRoot":"","sources":["../../lib/env-doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * HNSW - Hierarchical Navigable Small World
3
+ * Pure TypeScript, zero deps, base64 binary serialization.
4
+ *
5
+ * Based on: Malkov & Yashunin, "Efficient and robust approximate nearest
6
+ * neighbor search using Hierarchical Navigable Small World graphs" (2018)
7
+ */
8
+ export interface HNSWConfig {
9
+ /** Vector dimensionality */
10
+ dim: number;
11
+ /** Max connections per node per layer (default: 16) */
12
+ M: number;
13
+ /** Max connections for layer 0 (default: 2*M) */
14
+ M0: number;
15
+ /** Size of dynamic candidate list during construction (default: 200) */
16
+ efConstruction: number;
17
+ /** Normalization factor for level generation: 1/ln(M) */
18
+ mL: number;
19
+ }
20
+ export interface HNSWNode {
21
+ id: number;
22
+ vector: Float32Array;
23
+ level: number;
24
+ /** neighbors[layer] = array of neighbor node ids */
25
+ neighbors: number[][];
26
+ /** Optional metadata attached to the node */
27
+ metadata?: Record<string, unknown>;
28
+ }
29
+ export interface SearchResult {
30
+ id: number;
31
+ distance: number;
32
+ metadata?: Record<string, unknown>;
33
+ }
34
+ /** Cosine distance = 1 - cosine_similarity. Range [0, 2]. */
35
+ export declare function cosineDistance(a: Float32Array, b: Float32Array): number;
36
+ /** Euclidean (L2) distance squared — skip sqrt for perf since we only compare. */
37
+ export declare function euclideanDistance(a: Float32Array, b: Float32Array): number;
38
+ export type DistanceFunction = (a: Float32Array, b: Float32Array) => number;
39
+ export declare class HNSW {
40
+ private config;
41
+ private nodes;
42
+ private entryPointId;
43
+ private maxLevel;
44
+ private nextId;
45
+ private distanceFn;
46
+ /** Optional callback fired after every insert — use for write-through persistence. */
47
+ onInsert?: (index: HNSW) => void;
48
+ constructor(dim: number, opts?: Partial<Pick<HNSWConfig, 'M' | 'efConstruction'>> & {
49
+ distance?: DistanceFunction;
50
+ });
51
+ get size(): number;
52
+ get dimensions(): number;
53
+ private randomLevel;
54
+ /** Greedy search on a single layer. Returns ef closest nodes. */
55
+ private searchLayer;
56
+ private selectNeighbors;
57
+ insert(vector: number[] | Float32Array, metadata?: Record<string, unknown>): number;
58
+ search(query: number[] | Float32Array, k?: number, efSearch?: number): SearchResult[];
59
+ getNode(id: number): HNSWNode | undefined;
60
+ /**
61
+ * Binary layout:
62
+ * [Header: 24 bytes]
63
+ * magic (4 bytes): "HNSW"
64
+ * version (4 bytes): 1
65
+ * dim (4 bytes)
66
+ * nodeCount (4 bytes)
67
+ * entryPointId (4 bytes, signed)
68
+ * maxLevel (4 bytes, signed)
69
+ *
70
+ * [Config: 16 bytes]
71
+ * M (4 bytes)
72
+ * M0 (4 bytes)
73
+ * efConstruction (4 bytes)
74
+ * nextId (4 bytes)
75
+ *
76
+ * [Nodes: variable]
77
+ * For each node:
78
+ * id (4 bytes)
79
+ * level (4 bytes)
80
+ * metadataLength (4 bytes) — byte length of JSON metadata string
81
+ * metadata (metadataLength bytes) — UTF-8 JSON
82
+ * vector (dim * 4 bytes) — float32
83
+ * For each layer 0..level:
84
+ * neighborCount (4 bytes)
85
+ * neighborIds (neighborCount * 4 bytes)
86
+ */
87
+ serialize(): string;
88
+ static deserialize(base64: string, distance?: DistanceFunction): HNSW;
89
+ }
90
+ export default HNSW;
91
+ //# sourceMappingURL=hnsw.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hnsw.d.ts","sourceRoot":"","sources":["../../lib/hnsw.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,UAAU;IACzB,4BAA4B;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,CAAC,EAAE,MAAM,CAAC;IACV,iDAAiD;IACjD,EAAE,EAAE,MAAM,CAAC;IACX,wEAAwE;IACxE,cAAc,EAAE,MAAM,CAAC;IACvB,yDAAyD;IACzD,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC;IACtB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAID,6DAA6D;AAC7D,wBAAgB,cAAc,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM,CAUvE;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM,CAO1E;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,KAAK,MAAM,CAAC;AA6G5E,qBAAa,IAAI;IACf,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,UAAU,CAAmB;IAErC,sFAAsF;IAC/E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,IAAI,CAAC;gBAGtC,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,GAAG,gBAAgB,CAAC,CAAC,GAAG;QAAE,QAAQ,CAAC,EAAE,gBAAgB,CAAA;KAAO;IAahG,IAAI,IAAI,IAAI,MAAM,CAA4B;IAC9C,IAAI,UAAU,IAAI,MAAM,CAA4B;IAIpD,OAAO,CAAC,WAAW;IAQnB,iEAAiE;IACjE,OAAO,CAAC,WAAW;IA+CnB,OAAO,CAAC,eAAe;IAQvB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,YAAY,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM;IA0EnF,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,YAAY,EAAE,CAAC,GAAE,MAAW,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE;IA4BzF,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAMzC;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,SAAS,IAAI,MAAM;IAsEnB,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,gBAAgB,GAAG,IAAI;CAoEtE;AA+BD,eAAe,IAAI,CAAC"}