@pentoshi/clai 0.10.5 → 0.11.1

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 (78) hide show
  1. package/README.md +32 -0
  2. package/dist/agent/runner.d.ts +2 -0
  3. package/dist/agent/runner.js +124 -21
  4. package/dist/agent/runner.js.map +1 -1
  5. package/dist/commands/providers.js +28 -0
  6. package/dist/commands/providers.js.map +1 -1
  7. package/dist/commands/search-providers.d.ts +50 -0
  8. package/dist/commands/search-providers.js +134 -0
  9. package/dist/commands/search-providers.js.map +1 -0
  10. package/dist/commands/update.js +1 -1
  11. package/dist/index.js +8 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/llm/provider.js +9 -6
  14. package/dist/llm/provider.js.map +1 -1
  15. package/dist/prompts/index.d.ts +1 -1
  16. package/dist/prompts/index.js +7 -1
  17. package/dist/prompts/index.js.map +1 -1
  18. package/dist/safety/classifier.js +40 -0
  19. package/dist/safety/classifier.js.map +1 -1
  20. package/dist/store/config.d.ts +5 -0
  21. package/dist/store/config.js +7 -0
  22. package/dist/store/config.js.map +1 -1
  23. package/dist/store/keys.d.ts +65 -0
  24. package/dist/store/keys.js +164 -28
  25. package/dist/store/keys.js.map +1 -1
  26. package/dist/tools/http.d.ts +12 -1
  27. package/dist/tools/http.js +8 -43
  28. package/dist/tools/http.js.map +1 -1
  29. package/dist/tools/registry.js +52 -0
  30. package/dist/tools/registry.js.map +1 -1
  31. package/dist/tools/shell.d.ts +25 -0
  32. package/dist/tools/shell.js +155 -6
  33. package/dist/tools/shell.js.map +1 -1
  34. package/dist/tools/web/audit.d.ts +154 -0
  35. package/dist/tools/web/audit.js +147 -0
  36. package/dist/tools/web/audit.js.map +1 -0
  37. package/dist/tools/web/budget.d.ts +76 -0
  38. package/dist/tools/web/budget.js +187 -0
  39. package/dist/tools/web/budget.js.map +1 -0
  40. package/dist/tools/web/capture.d.ts +201 -0
  41. package/dist/tools/web/capture.js +380 -0
  42. package/dist/tools/web/capture.js.map +1 -0
  43. package/dist/tools/web/fetch-core.d.ts +66 -0
  44. package/dist/tools/web/fetch-core.js +1123 -0
  45. package/dist/tools/web/fetch-core.js.map +1 -0
  46. package/dist/tools/web/fetch.d.ts +42 -0
  47. package/dist/tools/web/fetch.js +115 -0
  48. package/dist/tools/web/fetch.js.map +1 -0
  49. package/dist/tools/web/providers/brave.d.ts +46 -0
  50. package/dist/tools/web/providers/brave.js +263 -0
  51. package/dist/tools/web/providers/brave.js.map +1 -0
  52. package/dist/tools/web/providers/duckduckgo.d.ts +47 -0
  53. package/dist/tools/web/providers/duckduckgo.js +248 -0
  54. package/dist/tools/web/providers/duckduckgo.js.map +1 -0
  55. package/dist/tools/web/providers/provider.d.ts +99 -0
  56. package/dist/tools/web/providers/provider.js +38 -0
  57. package/dist/tools/web/providers/provider.js.map +1 -0
  58. package/dist/tools/web/providers/tavily.d.ts +52 -0
  59. package/dist/tools/web/providers/tavily.js +285 -0
  60. package/dist/tools/web/providers/tavily.js.map +1 -0
  61. package/dist/tools/web/readable.d.ts +67 -0
  62. package/dist/tools/web/readable.js +248 -0
  63. package/dist/tools/web/readable.js.map +1 -0
  64. package/dist/tools/web/redact.d.ts +120 -0
  65. package/dist/tools/web/redact.js +155 -0
  66. package/dist/tools/web/redact.js.map +1 -0
  67. package/dist/tools/web/search.d.ts +51 -0
  68. package/dist/tools/web/search.js +389 -0
  69. package/dist/tools/web/search.js.map +1 -0
  70. package/dist/tools/web/ssrf-guard.d.ts +85 -0
  71. package/dist/tools/web/ssrf-guard.js +265 -0
  72. package/dist/tools/web/ssrf-guard.js.map +1 -0
  73. package/dist/tools/web/types.d.ts +331 -0
  74. package/dist/tools/web/types.js +71 -0
  75. package/dist/tools/web/types.js.map +1 -0
  76. package/dist/ui/spinner.js +87 -14
  77. package/dist/ui/spinner.js.map +1 -1
  78. package/package.json +3 -1
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Header / cookie redaction and audit-log stripping for `web.fetch`.
3
+ *
4
+ * This module is the single source of truth for the
5
+ * {@link HeaderMap} format described in Requirement 2.21 (lower-cased keys,
6
+ * repeat values joined with `", "`, per-value 4096-char cap with the
7
+ * literal `[...truncated]` marker). It also implements the redaction rules
8
+ * required by 2.22 / 2.32 and the audit-log strip required by 5.11–5.13.
9
+ *
10
+ * See `.kiro/specs/web-search-and-fetch/design.md` "Redaction strategy"
11
+ * for the broader picture.
12
+ */
13
+ import { type CookieInfo, type CookieSameSite, type HeaderMap } from "./types.js";
14
+ /**
15
+ * Case-insensitive set of header names whose values are considered
16
+ * sensitive. Stored lower-cased so callers can match them against
17
+ * already-normalized {@link HeaderMap} keys without re-casing.
18
+ *
19
+ * Mirrors Requirement 5.12 / design "Redaction strategy".
20
+ */
21
+ export declare const SENSITIVE_HEADERS: ReadonlySet<string>;
22
+ /**
23
+ * Ordered list of `[name, value]` pairs, the most general header input
24
+ * shape. Repeats of the same name are preserved in the order observed and
25
+ * are joined by {@link applyToHeaders} per RFC 7230.
26
+ *
27
+ * Callers that have a Node `http.IncomingMessage.rawHeaders` array (a flat
28
+ * `string[]` of alternating name/value entries) can convert it with
29
+ *
30
+ * ```ts
31
+ * const pairs: HeaderEntries = [];
32
+ * for (let i = 0; i < raw.length; i += 2) pairs.push([raw[i]!, raw[i + 1]!]);
33
+ * ```
34
+ */
35
+ export type HeaderEntries = ReadonlyArray<readonly [string, string]>;
36
+ /**
37
+ * Reduced cookie shape produced by {@link stripForAudit}: only the
38
+ * non-secret public attributes named by Requirement 5.11 are kept. The
39
+ * cookie's `value`, `expires`, and `maxAge` fields are intentionally
40
+ * absent — the audit log never carries cookie values nor any field that
41
+ * could leak session lifetime back to a third party.
42
+ */
43
+ export interface AuditSafeCookie {
44
+ name: string;
45
+ domain?: string;
46
+ path?: string;
47
+ httpOnly?: boolean;
48
+ secure?: boolean;
49
+ sameSite?: CookieSameSite;
50
+ }
51
+ /**
52
+ * Output shape of {@link stripForAudit}: a header map with every
53
+ * sensitive header dropped and a cookies list with every value-bearing
54
+ * field removed. Both fields are always present (possibly empty) so the
55
+ * caller can spread them into an audit payload without `undefined` checks.
56
+ */
57
+ export interface AuditSafePayload {
58
+ headers: HeaderMap;
59
+ cookies: AuditSafeCookie[];
60
+ }
61
+ /**
62
+ * Normalize a set of HTTP response headers into the canonical
63
+ * {@link HeaderMap} format documented in Requirement 2.21.
64
+ *
65
+ * Steps applied, in order:
66
+ * 1. Lower-case every header name.
67
+ * 2. Join repeated values for the same name with `", "` (RFC 7230).
68
+ * 3. If `redactSensitive=true` and the lower-cased name is in
69
+ * {@link SENSITIVE_HEADERS}, replace the joined value with the literal
70
+ * {@link REDACTED_PLACEHOLDER}.
71
+ * 4. Otherwise, if the joined value's character length exceeds
72
+ * {@link MAX_HEADER_VALUE_LENGTH}, slice it to the cap and append
73
+ * {@link TRUNCATION_MARKER}.
74
+ *
75
+ * Accepts either a flat {@link HeaderMap} (already deduplicated by the
76
+ * caller) or a list of `[name, value]` pairs ({@link HeaderEntries}). The
77
+ * pair form preserves repeats — a `Set-Cookie` header that appears three
78
+ * times will produce three entries that this function joins. The flat-map
79
+ * form is convenient for callers that already have a `Record<string,
80
+ * string>`; in that case there are no repeats to join.
81
+ *
82
+ * The function is the single source of truth for the
83
+ * {@link HeaderMap} format. Both the user-facing fetch result and the
84
+ * audit-log path go through it (the audit path additionally calls
85
+ * {@link stripForAudit} to drop sensitive entries entirely).
86
+ */
87
+ export declare function applyToHeaders(input: HeaderMap | HeaderEntries, redactSensitive: boolean): HeaderMap;
88
+ /**
89
+ * Apply the cookie-value redaction rule from Requirement 2.32.
90
+ *
91
+ * Returns a fresh array of fresh {@link CookieInfo} objects so the caller
92
+ * can mutate the result without aliasing the inputs:
93
+ * - When `redactSensitive=true`: every entry's `value` is replaced with
94
+ * the literal {@link REDACTED_PLACEHOLDER}; all other attributes
95
+ * (`name`, `domain`, `path`, `expires`, `maxAge`, `httpOnly`, `secure`,
96
+ * `sameSite`) are preserved unchanged.
97
+ * - When `redactSensitive=false`: every entry is shallow-copied verbatim.
98
+ */
99
+ export declare function applyToCookies(cookies: readonly CookieInfo[], redactSensitive: boolean): CookieInfo[];
100
+ /**
101
+ * Build the audit-log-safe view of a fetch's headers and cookies.
102
+ *
103
+ * Always-on guarantees, regardless of `redactSensitive`:
104
+ * - Every {@link SENSITIVE_HEADERS} entry is removed from the returned
105
+ * header map (key and value both gone). The value never appears in any
106
+ * form, including as `[REDACTED]` — the audit log simply does not
107
+ * advertise that the header was present (Requirement 5.12).
108
+ * - Every cookie is reduced to the {@link AuditSafeCookie} shape:
109
+ * `{name, domain, path, httpOnly, secure, sameSite}`. Cookie values,
110
+ * `expires`, and `maxAge` are dropped (Requirements 5.11, 5.12).
111
+ *
112
+ * The remaining (non-sensitive) headers are passed through with their
113
+ * keys lower-cased so the caller does not need to re-normalize. Header
114
+ * values are not re-truncated here — they have already been processed by
115
+ * {@link applyToHeaders} on the way into `metadata.headers`.
116
+ *
117
+ * Both arguments are tolerant of `undefined` so the caller can hand off
118
+ * an optional `metadata.headers` / `metadata.cookies` directly.
119
+ */
120
+ export declare function stripForAudit(headers: HeaderMap | undefined, cookies: readonly CookieInfo[] | undefined): AuditSafePayload;
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Header / cookie redaction and audit-log stripping for `web.fetch`.
3
+ *
4
+ * This module is the single source of truth for the
5
+ * {@link HeaderMap} format described in Requirement 2.21 (lower-cased keys,
6
+ * repeat values joined with `", "`, per-value 4096-char cap with the
7
+ * literal `[...truncated]` marker). It also implements the redaction rules
8
+ * required by 2.22 / 2.32 and the audit-log strip required by 5.11–5.13.
9
+ *
10
+ * See `.kiro/specs/web-search-and-fetch/design.md` "Redaction strategy"
11
+ * for the broader picture.
12
+ */
13
+ import { MAX_HEADER_VALUE_LENGTH, REDACTED_PLACEHOLDER, TRUNCATION_MARKER, } from "./types.js";
14
+ /**
15
+ * Case-insensitive set of header names whose values are considered
16
+ * sensitive. Stored lower-cased so callers can match them against
17
+ * already-normalized {@link HeaderMap} keys without re-casing.
18
+ *
19
+ * Mirrors Requirement 5.12 / design "Redaction strategy".
20
+ */
21
+ export const SENSITIVE_HEADERS = new Set([
22
+ "cookie",
23
+ "set-cookie",
24
+ "authorization",
25
+ "proxy-authorization",
26
+ ]);
27
+ /**
28
+ * Normalize a set of HTTP response headers into the canonical
29
+ * {@link HeaderMap} format documented in Requirement 2.21.
30
+ *
31
+ * Steps applied, in order:
32
+ * 1. Lower-case every header name.
33
+ * 2. Join repeated values for the same name with `", "` (RFC 7230).
34
+ * 3. If `redactSensitive=true` and the lower-cased name is in
35
+ * {@link SENSITIVE_HEADERS}, replace the joined value with the literal
36
+ * {@link REDACTED_PLACEHOLDER}.
37
+ * 4. Otherwise, if the joined value's character length exceeds
38
+ * {@link MAX_HEADER_VALUE_LENGTH}, slice it to the cap and append
39
+ * {@link TRUNCATION_MARKER}.
40
+ *
41
+ * Accepts either a flat {@link HeaderMap} (already deduplicated by the
42
+ * caller) or a list of `[name, value]` pairs ({@link HeaderEntries}). The
43
+ * pair form preserves repeats — a `Set-Cookie` header that appears three
44
+ * times will produce three entries that this function joins. The flat-map
45
+ * form is convenient for callers that already have a `Record<string,
46
+ * string>`; in that case there are no repeats to join.
47
+ *
48
+ * The function is the single source of truth for the
49
+ * {@link HeaderMap} format. Both the user-facing fetch result and the
50
+ * audit-log path go through it (the audit path additionally calls
51
+ * {@link stripForAudit} to drop sensitive entries entirely).
52
+ */
53
+ export function applyToHeaders(input, redactSensitive) {
54
+ // Group repeated header names. Ordering is preserved by the Map.
55
+ const grouped = new Map();
56
+ const entries = Array.isArray(input)
57
+ ? input
58
+ : Object.entries(input);
59
+ for (const [name, value] of entries) {
60
+ if (typeof name !== "string" || typeof value !== "string")
61
+ continue;
62
+ const key = name.toLowerCase();
63
+ const existing = grouped.get(key);
64
+ if (existing) {
65
+ existing.push(value);
66
+ }
67
+ else {
68
+ grouped.set(key, [value]);
69
+ }
70
+ }
71
+ const out = {};
72
+ for (const [key, values] of grouped) {
73
+ if (redactSensitive && SENSITIVE_HEADERS.has(key)) {
74
+ // The redacted placeholder is shorter than the cap and is a fixed
75
+ // marker; do not run it through the truncator.
76
+ out[key] = REDACTED_PLACEHOLDER;
77
+ continue;
78
+ }
79
+ const joined = values.join(", ");
80
+ out[key] =
81
+ joined.length > MAX_HEADER_VALUE_LENGTH
82
+ ? joined.slice(0, MAX_HEADER_VALUE_LENGTH) + TRUNCATION_MARKER
83
+ : joined;
84
+ }
85
+ return out;
86
+ }
87
+ /**
88
+ * Apply the cookie-value redaction rule from Requirement 2.32.
89
+ *
90
+ * Returns a fresh array of fresh {@link CookieInfo} objects so the caller
91
+ * can mutate the result without aliasing the inputs:
92
+ * - When `redactSensitive=true`: every entry's `value` is replaced with
93
+ * the literal {@link REDACTED_PLACEHOLDER}; all other attributes
94
+ * (`name`, `domain`, `path`, `expires`, `maxAge`, `httpOnly`, `secure`,
95
+ * `sameSite`) are preserved unchanged.
96
+ * - When `redactSensitive=false`: every entry is shallow-copied verbatim.
97
+ */
98
+ export function applyToCookies(cookies, redactSensitive) {
99
+ if (!redactSensitive) {
100
+ return cookies.map((c) => ({ ...c }));
101
+ }
102
+ return cookies.map((c) => ({ ...c, value: REDACTED_PLACEHOLDER }));
103
+ }
104
+ /**
105
+ * Build the audit-log-safe view of a fetch's headers and cookies.
106
+ *
107
+ * Always-on guarantees, regardless of `redactSensitive`:
108
+ * - Every {@link SENSITIVE_HEADERS} entry is removed from the returned
109
+ * header map (key and value both gone). The value never appears in any
110
+ * form, including as `[REDACTED]` — the audit log simply does not
111
+ * advertise that the header was present (Requirement 5.12).
112
+ * - Every cookie is reduced to the {@link AuditSafeCookie} shape:
113
+ * `{name, domain, path, httpOnly, secure, sameSite}`. Cookie values,
114
+ * `expires`, and `maxAge` are dropped (Requirements 5.11, 5.12).
115
+ *
116
+ * The remaining (non-sensitive) headers are passed through with their
117
+ * keys lower-cased so the caller does not need to re-normalize. Header
118
+ * values are not re-truncated here — they have already been processed by
119
+ * {@link applyToHeaders} on the way into `metadata.headers`.
120
+ *
121
+ * Both arguments are tolerant of `undefined` so the caller can hand off
122
+ * an optional `metadata.headers` / `metadata.cookies` directly.
123
+ */
124
+ export function stripForAudit(headers, cookies) {
125
+ const safeHeaders = {};
126
+ if (headers) {
127
+ for (const [key, value] of Object.entries(headers)) {
128
+ if (typeof key !== "string" || typeof value !== "string")
129
+ continue;
130
+ const lower = key.toLowerCase();
131
+ if (SENSITIVE_HEADERS.has(lower))
132
+ continue;
133
+ safeHeaders[lower] = value;
134
+ }
135
+ }
136
+ const safeCookies = [];
137
+ if (cookies) {
138
+ for (const c of cookies) {
139
+ const entry = { name: c.name };
140
+ if (c.domain !== undefined)
141
+ entry.domain = c.domain;
142
+ if (c.path !== undefined)
143
+ entry.path = c.path;
144
+ if (c.httpOnly !== undefined)
145
+ entry.httpOnly = c.httpOnly;
146
+ if (c.secure !== undefined)
147
+ entry.secure = c.secure;
148
+ if (c.sameSite !== undefined)
149
+ entry.sameSite = c.sameSite;
150
+ safeCookies.push(entry);
151
+ }
152
+ }
153
+ return { headers: safeHeaders, cookies: safeCookies };
154
+ }
155
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../../../src/tools/web/redact.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * `web.search` registry handler.
3
+ *
4
+ * Resolves the active {@link SearchProviderId}, looks up the API key (if
5
+ * needed), dispatches a single outbound request via the registered
6
+ * adapter, validates each returned hit per Requirement 7.3, truncates to
7
+ * `maxResults`, and emits exactly one structured audit-log entry per
8
+ * invocation (Requirement 5.5).
9
+ *
10
+ * Error handling mirrors the design's error matrix: timeouts (1.8),
11
+ * missing keys (3.4), provider auth failures (6.1), rate limiting (6.2),
12
+ * network failures (6.3), parse failures (6.5), 5xx (6.6), and other
13
+ * non-2xx (1.9). Every failure surfaces as `ok=false` with a categorical
14
+ * `error.kind` and a human-readable message that names the active
15
+ * provider.
16
+ *
17
+ * Per Requirement 6.7 the handler issues exactly one outbound request
18
+ * attempt — there is no retry on transient failure.
19
+ *
20
+ * The provider modules register themselves into the shared
21
+ * {@link searchProviders} registry on import; we eagerly import them
22
+ * here so `web.search` can be invoked without any lazy-load surprises.
23
+ */
24
+ import type { ToolResult } from "../../types.js";
25
+ import type { ToolRunOptions } from "../registry.js";
26
+ import { type SearchProvider } from "./providers/provider.js";
27
+ import "./providers/duckduckgo.js";
28
+ import "./providers/brave.js";
29
+ import "./providers/tavily.js";
30
+ import { type SearchProviderId, type WebSearchArgs, type WebSearchErrorKind, type WebSearchOutcome } from "./types.js";
31
+ /**
32
+ * Optional injection points for tests so the search dispatch can be
33
+ * exercised without invoking the real provider modules. Production
34
+ * callers never pass these.
35
+ */
36
+ export interface WebSearchOptions extends ToolRunOptions {
37
+ /** Override the active provider lookup. */
38
+ provider?: SearchProviderId;
39
+ /** Override the registered {@link SearchProvider} for the active id. */
40
+ providerOverride?: SearchProvider;
41
+ /** Override the API-key resolver. Returns the raw key (or undefined). */
42
+ resolveKey?: (id: SearchProviderId) => Promise<string | undefined>;
43
+ /** Wall-clock timeout in milliseconds. Default: {@link SEARCH_TIMEOUT_MS}. */
44
+ timeoutMs?: number;
45
+ }
46
+ /**
47
+ * Run `web.search`. Always emits a single audit-log entry. Never
48
+ * throws — every failure mode surfaces as `ok=false`.
49
+ */
50
+ export declare function webSearch(args: WebSearchArgs, options?: WebSearchOptions): Promise<ToolResult>;
51
+ export type { WebSearchOutcome, WebSearchErrorKind };