@husk-ai/sessions 0.1.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.
- package/dist/chat.d.ts +11 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +2 -0
- package/dist/chat.js.map +1 -0
- package/dist/distill.d.ts +130 -0
- package/dist/distill.d.ts.map +1 -0
- package/dist/distill.js +773 -0
- package/dist/distill.js.map +1 -0
- package/dist/distiller.d.ts +18 -0
- package/dist/distiller.d.ts.map +1 -0
- package/dist/distiller.js +23 -0
- package/dist/distiller.js.map +1 -0
- package/dist/formatter.d.ts +8 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +11 -0
- package/dist/formatter.js.map +1 -0
- package/dist/importers/chatgpt.d.ts +12 -0
- package/dist/importers/chatgpt.d.ts.map +1 -0
- package/dist/importers/chatgpt.js +223 -0
- package/dist/importers/chatgpt.js.map +1 -0
- package/dist/importers/claude.d.ts +37 -0
- package/dist/importers/claude.d.ts.map +1 -0
- package/dist/importers/claude.js +512 -0
- package/dist/importers/claude.js.map +1 -0
- package/dist/importers/cursor.d.ts +12 -0
- package/dist/importers/cursor.d.ts.map +1 -0
- package/dist/importers/cursor.js +229 -0
- package/dist/importers/cursor.js.map +1 -0
- package/dist/importers/fsutil.d.ts +22 -0
- package/dist/importers/fsutil.d.ts.map +1 -0
- package/dist/importers/fsutil.js +79 -0
- package/dist/importers/fsutil.js.map +1 -0
- package/dist/importers/gemini.d.ts +13 -0
- package/dist/importers/gemini.d.ts.map +1 -0
- package/dist/importers/gemini.js +228 -0
- package/dist/importers/gemini.js.map +1 -0
- package/dist/importers/index.d.ts +70 -0
- package/dist/importers/index.d.ts.map +1 -0
- package/dist/importers/index.js +258 -0
- package/dist/importers/index.js.map +1 -0
- package/dist/importers/markdown.d.ts +14 -0
- package/dist/importers/markdown.d.ts.map +1 -0
- package/dist/importers/markdown.js +188 -0
- package/dist/importers/markdown.js.map +1 -0
- package/dist/importers/universal.d.ts +16 -0
- package/dist/importers/universal.d.ts.map +1 -0
- package/dist/importers/universal.js +106 -0
- package/dist/importers/universal.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts.d.ts +151 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +109 -0
- package/dist/prompts.js.map +1 -0
- package/dist/redactor.d.ts +38 -0
- package/dist/redactor.d.ts.map +1 -0
- package/dist/redactor.js +144 -0
- package/dist/redactor.js.map +1 -0
- package/dist/scaffold.d.ts +40 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +0 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/serialize.d.ts +15 -0
- package/dist/serialize.d.ts.map +1 -0
- package/dist/serialize.js +122 -0
- package/dist/serialize.js.map +1 -0
- package/package.json +28 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { clampText, HuskError, id } from '@husk-ai/core';
|
|
3
|
+
import { parseMarkdownChat } from './markdown.js';
|
|
4
|
+
/**
|
|
5
|
+
* The last resort: a chat pasted out of an interface nobody wrote an importer
|
|
6
|
+
* for. It tries the markdown heuristics first, because they cost nothing and
|
|
7
|
+
* are right surprisingly often, and only then asks a model.
|
|
8
|
+
*
|
|
9
|
+
* With no model reachable it still returns whatever the heuristics found. This
|
|
10
|
+
* package has no hard dependency on a network.
|
|
11
|
+
*/
|
|
12
|
+
const SYSTEM = `You convert a raw text dump of a chat into JSON.
|
|
13
|
+
Return a JSON array of messages and nothing else -- no prose, no markdown fence.
|
|
14
|
+
Each element: {"role":"user"|"assistant"|"system"|"tool","content":"..."}.
|
|
15
|
+
Preserve the original wording. Do not summarise, translate, or invent turns.`;
|
|
16
|
+
function stripFence(s) {
|
|
17
|
+
const trimmed = s.trim();
|
|
18
|
+
const fenced = /^```(?:json)?\s*\n([\s\S]*?)\n?```$/.exec(trimmed);
|
|
19
|
+
return (fenced?.[1] ?? trimmed).trim();
|
|
20
|
+
}
|
|
21
|
+
function mapRole(role) {
|
|
22
|
+
switch (String(role ?? '').toLowerCase()) {
|
|
23
|
+
case 'assistant':
|
|
24
|
+
return 'assistant';
|
|
25
|
+
case 'system':
|
|
26
|
+
return 'system';
|
|
27
|
+
case 'tool':
|
|
28
|
+
return 'tool';
|
|
29
|
+
default:
|
|
30
|
+
return 'user';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export class UniversalImporter {
|
|
34
|
+
model;
|
|
35
|
+
opts;
|
|
36
|
+
id = 'universal';
|
|
37
|
+
displayName = 'Universal (model-assisted)';
|
|
38
|
+
/** Injected so tests never touch a network and callers control the budget. */
|
|
39
|
+
constructor(model, opts = {}) {
|
|
40
|
+
this.model = model;
|
|
41
|
+
this.opts = opts;
|
|
42
|
+
}
|
|
43
|
+
async detect() {
|
|
44
|
+
// Deliberately below every real importer. This one only wins when nothing
|
|
45
|
+
// else recognised the input at all.
|
|
46
|
+
return 0.05;
|
|
47
|
+
}
|
|
48
|
+
async parse(input) {
|
|
49
|
+
let content = input.content;
|
|
50
|
+
if (content === undefined) {
|
|
51
|
+
if (!input.path) {
|
|
52
|
+
throw new HuskError('E_IMPORT_FAILED', 'Universal import needs a path or content.', {
|
|
53
|
+
hint: 'Pass { content } with the pasted chat text.',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
content = await readFile(input.path, 'utf8');
|
|
57
|
+
}
|
|
58
|
+
if (!content.trim())
|
|
59
|
+
return [];
|
|
60
|
+
const heuristic = parseMarkdownChat(content, input.path);
|
|
61
|
+
if (heuristic && heuristic.messages.length >= 2) {
|
|
62
|
+
heuristic.source = 'universal';
|
|
63
|
+
heuristic.meta = { ...(heuristic.meta ?? {}), extractedBy: 'heuristic' };
|
|
64
|
+
return [heuristic];
|
|
65
|
+
}
|
|
66
|
+
if (!this.model)
|
|
67
|
+
return heuristic ? [{ ...heuristic, source: 'universal' }] : [];
|
|
68
|
+
const { text: clamped } = clampText(content, this.opts.maxBytes ?? 120_000);
|
|
69
|
+
try {
|
|
70
|
+
const res = await this.model.chat({
|
|
71
|
+
model: this.opts.model ?? 'auto',
|
|
72
|
+
system: SYSTEM,
|
|
73
|
+
messages: [{ role: 'user', content: clamped }],
|
|
74
|
+
temperature: 0,
|
|
75
|
+
responseFormat: { type: 'json' },
|
|
76
|
+
});
|
|
77
|
+
const parsed = JSON.parse(stripFence(res.text));
|
|
78
|
+
const rows = Array.isArray(parsed)
|
|
79
|
+
? parsed
|
|
80
|
+
: typeof parsed === 'object' && parsed !== null && Array.isArray(parsed.messages)
|
|
81
|
+
? (parsed.messages)
|
|
82
|
+
: [];
|
|
83
|
+
const messages = rows
|
|
84
|
+
.filter((r) => typeof r === 'object' && r !== null)
|
|
85
|
+
.map((r) => ({ role: mapRole(r.role), content: String(r.content ?? '') }))
|
|
86
|
+
.filter((m) => m.content.trim() !== '');
|
|
87
|
+
if (!messages.length)
|
|
88
|
+
return heuristic ? [{ ...heuristic, source: 'universal' }] : [];
|
|
89
|
+
return [
|
|
90
|
+
{
|
|
91
|
+
id: id('universal'),
|
|
92
|
+
source: 'universal',
|
|
93
|
+
messages,
|
|
94
|
+
...(input.path ? { origin: input.path } : {}),
|
|
95
|
+
meta: { extractedBy: 'model' },
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// A model that is down, rate limited, or hallucinating JSON must not lose
|
|
101
|
+
// the input. Hand back whatever the heuristics managed.
|
|
102
|
+
return heuristic ? [{ ...heuristic, source: 'universal' }] : [];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=universal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"universal.js","sourceRoot":"","sources":["../../src/importers/universal.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @husk-ai/sessions -- a chat becomes a bot.
|
|
3
|
+
*
|
|
4
|
+
* Import a transcript (Claude Code, ChatGPT, Cursor, Gemini, markdown),
|
|
5
|
+
* reconstruct the thread that actually happened, distil it into a
|
|
6
|
+
* `DistilledAgent`, and write it out as a husk.yaml.
|
|
7
|
+
*/
|
|
8
|
+
export type { ChatLike } from './chat.js';
|
|
9
|
+
export * from './importers/index.js';
|
|
10
|
+
export * from './distill.js';
|
|
11
|
+
export * from './prompts.js';
|
|
12
|
+
export * from './serialize.js';
|
|
13
|
+
export * from './redactor.js';
|
|
14
|
+
export * from './scaffold.js';
|
|
15
|
+
export * from './distiller.js';
|
|
16
|
+
export * from './formatter.js';
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,YAAY,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,cAAc,sBAAsB,CAAC;AACrC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @husk-ai/sessions -- a chat becomes a bot.
|
|
3
|
+
*
|
|
4
|
+
* Import a transcript (Claude Code, ChatGPT, Cursor, Gemini, markdown),
|
|
5
|
+
* reconstruct the thread that actually happened, distil it into a
|
|
6
|
+
* `DistilledAgent`, and write it out as a husk.yaml.
|
|
7
|
+
*/
|
|
8
|
+
export * from './importers/index.js';
|
|
9
|
+
export * from './distill.js';
|
|
10
|
+
export * from './prompts.js';
|
|
11
|
+
export * from './serialize.js';
|
|
12
|
+
export * from './redactor.js';
|
|
13
|
+
export * from './scaffold.js';
|
|
14
|
+
export * from './distiller.js';
|
|
15
|
+
export * from './formatter.js';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,cAAc,sBAAsB,CAAC;AACrC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { TranscriptMessage } from '@husk-ai/core';
|
|
3
|
+
/**
|
|
4
|
+
* Every prompt the distiller sends, as a pure function of its inputs.
|
|
5
|
+
*
|
|
6
|
+
* They live here so they can be diffed, reviewed and unit-tested without a
|
|
7
|
+
* network. A prompt buried in a method is a prompt nobody ever reads again.
|
|
8
|
+
*/
|
|
9
|
+
export declare const CandidateSchema: z.ZodObject<{
|
|
10
|
+
name: z.ZodDefault<z.ZodString>;
|
|
11
|
+
description: z.ZodDefault<z.ZodString>;
|
|
12
|
+
/** One rule per entry, imperative, in the user's own words where possible. */
|
|
13
|
+
personaRules: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
14
|
+
knowledge: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
15
|
+
title: z.ZodString;
|
|
16
|
+
content: z.ZodString;
|
|
17
|
+
}, "strip", z.ZodTypeAny, {
|
|
18
|
+
title: string;
|
|
19
|
+
content: string;
|
|
20
|
+
}, {
|
|
21
|
+
title: string;
|
|
22
|
+
content: string;
|
|
23
|
+
}>, "many">>;
|
|
24
|
+
examples: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
25
|
+
user: z.ZodString;
|
|
26
|
+
assistant: z.ZodString;
|
|
27
|
+
}, "strip", z.ZodTypeAny, {
|
|
28
|
+
user: string;
|
|
29
|
+
assistant: string;
|
|
30
|
+
}, {
|
|
31
|
+
user: string;
|
|
32
|
+
assistant: string;
|
|
33
|
+
}>, "many">>;
|
|
34
|
+
tools: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
35
|
+
needsComputer: z.ZodDefault<z.ZodBoolean>;
|
|
36
|
+
notes: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
37
|
+
}, "strip", z.ZodTypeAny, {
|
|
38
|
+
name: string;
|
|
39
|
+
description: string;
|
|
40
|
+
personaRules: string[];
|
|
41
|
+
knowledge: {
|
|
42
|
+
title: string;
|
|
43
|
+
content: string;
|
|
44
|
+
}[];
|
|
45
|
+
examples: {
|
|
46
|
+
user: string;
|
|
47
|
+
assistant: string;
|
|
48
|
+
}[];
|
|
49
|
+
tools: string[];
|
|
50
|
+
needsComputer: boolean;
|
|
51
|
+
notes: string[];
|
|
52
|
+
}, {
|
|
53
|
+
name?: string | undefined;
|
|
54
|
+
description?: string | undefined;
|
|
55
|
+
personaRules?: string[] | undefined;
|
|
56
|
+
knowledge?: {
|
|
57
|
+
title: string;
|
|
58
|
+
content: string;
|
|
59
|
+
}[] | undefined;
|
|
60
|
+
examples?: {
|
|
61
|
+
user: string;
|
|
62
|
+
assistant: string;
|
|
63
|
+
}[] | undefined;
|
|
64
|
+
tools?: string[] | undefined;
|
|
65
|
+
needsComputer?: boolean | undefined;
|
|
66
|
+
notes?: string[] | undefined;
|
|
67
|
+
}>;
|
|
68
|
+
export type Candidate = z.infer<typeof CandidateSchema>;
|
|
69
|
+
export declare const MergedSchema: z.ZodObject<{
|
|
70
|
+
name: z.ZodDefault<z.ZodString>;
|
|
71
|
+
description: z.ZodDefault<z.ZodString>;
|
|
72
|
+
/** One rule per entry, imperative, in the user's own words where possible. */
|
|
73
|
+
personaRules: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
74
|
+
knowledge: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
75
|
+
title: z.ZodString;
|
|
76
|
+
content: z.ZodString;
|
|
77
|
+
}, "strip", z.ZodTypeAny, {
|
|
78
|
+
title: string;
|
|
79
|
+
content: string;
|
|
80
|
+
}, {
|
|
81
|
+
title: string;
|
|
82
|
+
content: string;
|
|
83
|
+
}>, "many">>;
|
|
84
|
+
examples: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
85
|
+
user: z.ZodString;
|
|
86
|
+
assistant: z.ZodString;
|
|
87
|
+
}, "strip", z.ZodTypeAny, {
|
|
88
|
+
user: string;
|
|
89
|
+
assistant: string;
|
|
90
|
+
}, {
|
|
91
|
+
user: string;
|
|
92
|
+
assistant: string;
|
|
93
|
+
}>, "many">>;
|
|
94
|
+
tools: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
95
|
+
needsComputer: z.ZodDefault<z.ZodBoolean>;
|
|
96
|
+
notes: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
97
|
+
} & {
|
|
98
|
+
persona: z.ZodDefault<z.ZodString>;
|
|
99
|
+
confidence: z.ZodDefault<z.ZodNumber>;
|
|
100
|
+
}, "strip", z.ZodTypeAny, {
|
|
101
|
+
name: string;
|
|
102
|
+
description: string;
|
|
103
|
+
personaRules: string[];
|
|
104
|
+
knowledge: {
|
|
105
|
+
title: string;
|
|
106
|
+
content: string;
|
|
107
|
+
}[];
|
|
108
|
+
examples: {
|
|
109
|
+
user: string;
|
|
110
|
+
assistant: string;
|
|
111
|
+
}[];
|
|
112
|
+
tools: string[];
|
|
113
|
+
needsComputer: boolean;
|
|
114
|
+
notes: string[];
|
|
115
|
+
persona: string;
|
|
116
|
+
confidence: number;
|
|
117
|
+
}, {
|
|
118
|
+
name?: string | undefined;
|
|
119
|
+
description?: string | undefined;
|
|
120
|
+
personaRules?: string[] | undefined;
|
|
121
|
+
knowledge?: {
|
|
122
|
+
title: string;
|
|
123
|
+
content: string;
|
|
124
|
+
}[] | undefined;
|
|
125
|
+
examples?: {
|
|
126
|
+
user: string;
|
|
127
|
+
assistant: string;
|
|
128
|
+
}[] | undefined;
|
|
129
|
+
tools?: string[] | undefined;
|
|
130
|
+
needsComputer?: boolean | undefined;
|
|
131
|
+
notes?: string[] | undefined;
|
|
132
|
+
persona?: string | undefined;
|
|
133
|
+
confidence?: number | undefined;
|
|
134
|
+
}>;
|
|
135
|
+
export type Merged = z.infer<typeof MergedSchema>;
|
|
136
|
+
export interface WindowMeta {
|
|
137
|
+
index: number;
|
|
138
|
+
total: number;
|
|
139
|
+
title?: string;
|
|
140
|
+
source: string;
|
|
141
|
+
}
|
|
142
|
+
export declare function extractSystemPrompt(): string;
|
|
143
|
+
export declare function extractUserPrompt(windowText: string, meta: WindowMeta): string;
|
|
144
|
+
export declare function mergeSystemPrompt(): string;
|
|
145
|
+
export declare function mergeUserPrompt(candidates: Candidate[], meta: {
|
|
146
|
+
title?: string;
|
|
147
|
+
source: string;
|
|
148
|
+
}): string;
|
|
149
|
+
/** Render messages for a model: role-tagged, tool calls named, results clamped. */
|
|
150
|
+
export declare function renderWindow(messages: TranscriptMessage[], maxToolResultChars?: number): string;
|
|
151
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD;;;;;GAKG;AAEH,eAAO,MAAM,eAAe;;;IAG1B,8EAA8E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAS9E,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,eAAO,MAAM,YAAY;;;IAZvB,8EAA8E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAe9E,CAAC;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAElD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAID,wBAAgB,mBAAmB,IAAI,MAAM,CAsB5C;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAW9E;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAmB1C;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CASzG;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,iBAAiB,EAAE,EAAE,kBAAkB,SAAM,GAAG,MAAM,CAgB5F"}
|
package/dist/prompts.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Every prompt the distiller sends, as a pure function of its inputs.
|
|
4
|
+
*
|
|
5
|
+
* They live here so they can be diffed, reviewed and unit-tested without a
|
|
6
|
+
* network. A prompt buried in a method is a prompt nobody ever reads again.
|
|
7
|
+
*/
|
|
8
|
+
export const CandidateSchema = z.object({
|
|
9
|
+
name: z.string().default(''),
|
|
10
|
+
description: z.string().default(''),
|
|
11
|
+
/** One rule per entry, imperative, in the user's own words where possible. */
|
|
12
|
+
personaRules: z.array(z.string()).default([]),
|
|
13
|
+
knowledge: z
|
|
14
|
+
.array(z.object({ title: z.string(), content: z.string() }))
|
|
15
|
+
.default([]),
|
|
16
|
+
examples: z.array(z.object({ user: z.string(), assistant: z.string() })).default([]),
|
|
17
|
+
tools: z.array(z.string()).default([]),
|
|
18
|
+
needsComputer: z.boolean().default(false),
|
|
19
|
+
notes: z.array(z.string()).default([]),
|
|
20
|
+
});
|
|
21
|
+
export const MergedSchema = CandidateSchema.extend({
|
|
22
|
+
persona: z.string().default(''),
|
|
23
|
+
confidence: z.number().min(0).max(1).default(0.5),
|
|
24
|
+
});
|
|
25
|
+
const JSON_ONLY = 'Reply with one JSON object and nothing else. No prose, no markdown fence.';
|
|
26
|
+
export function extractSystemPrompt() {
|
|
27
|
+
return [
|
|
28
|
+
'You read one slice of a chat transcript and report what the user taught the assistant.',
|
|
29
|
+
'',
|
|
30
|
+
'You are building a reusable bot from a real conversation. Report only what the',
|
|
31
|
+
'transcript shows. Do not generalise, do not invent capabilities, do not write a',
|
|
32
|
+
'persona for an assistant that would be nice to have.',
|
|
33
|
+
'',
|
|
34
|
+
'Rules:',
|
|
35
|
+
'- personaRules: standing instructions the user gave ("always...", "never...",',
|
|
36
|
+
' "use X", "you are..."). Quote or lightly normalise the user\'s wording.',
|
|
37
|
+
' A one-off request for this task is not a standing instruction.',
|
|
38
|
+
'- knowledge: durable facts the USER supplied (systems, names, conventions,',
|
|
39
|
+
' constraints). Never facts the assistant produced.',
|
|
40
|
+
'- examples: an exchange worth imitating. Skip any turn the user then corrected.',
|
|
41
|
+
'- tools: tool names actually used in this slice, verbatim.',
|
|
42
|
+
'- notes: what this slice could not tell you.',
|
|
43
|
+
'',
|
|
44
|
+
JSON_ONLY,
|
|
45
|
+
'Shape: {"name":"","description":"","personaRules":[],"knowledge":[{"title":"","content":""}],',
|
|
46
|
+
'"examples":[{"user":"","assistant":""}],"tools":[],"needsComputer":false,"notes":[]}',
|
|
47
|
+
].join('\n');
|
|
48
|
+
}
|
|
49
|
+
export function extractUserPrompt(windowText, meta) {
|
|
50
|
+
return [
|
|
51
|
+
`Transcript: ${meta.title ?? '(untitled)'} (source: ${meta.source})`,
|
|
52
|
+
`Slice ${meta.index + 1} of ${meta.total}.`,
|
|
53
|
+
'',
|
|
54
|
+
'--- BEGIN SLICE ---',
|
|
55
|
+
windowText,
|
|
56
|
+
'--- END SLICE ---',
|
|
57
|
+
'',
|
|
58
|
+
'Report this slice as JSON.',
|
|
59
|
+
].join('\n');
|
|
60
|
+
}
|
|
61
|
+
export function mergeSystemPrompt() {
|
|
62
|
+
return [
|
|
63
|
+
'You merge per-slice reports of one conversation into a single bot definition.',
|
|
64
|
+
'',
|
|
65
|
+
'Rules:',
|
|
66
|
+
'- A rule that appears in several slices is a real standing instruction. Keep it.',
|
|
67
|
+
'- A rule that appears once and contradicts a later slice is stale. Drop it.',
|
|
68
|
+
'- Deduplicate aggressively. Twelve rules that say the same thing is one rule.',
|
|
69
|
+
'- persona: a system prompt written in the second person. Open with who the bot',
|
|
70
|
+
' is, then the rules as a short list. No filler, no "As an AI".',
|
|
71
|
+
'- Keep at most 8 knowledge items and 6 examples, the most reusable ones.',
|
|
72
|
+
'- confidence: 0..1, how much of a working bot the transcript actually supports.',
|
|
73
|
+
' Be honest. A transcript with no standing instructions is not a 0.9.',
|
|
74
|
+
'',
|
|
75
|
+
JSON_ONLY,
|
|
76
|
+
'Shape: {"name":"","description":"","persona":"","personaRules":[],',
|
|
77
|
+
'"knowledge":[{"title":"","content":""}],"examples":[{"user":"","assistant":""}],',
|
|
78
|
+
'"tools":[],"needsComputer":false,"confidence":0.5,"notes":[]}',
|
|
79
|
+
].join('\n');
|
|
80
|
+
}
|
|
81
|
+
export function mergeUserPrompt(candidates, meta) {
|
|
82
|
+
return [
|
|
83
|
+
`Transcript: ${meta.title ?? '(untitled)'} (source: ${meta.source})`,
|
|
84
|
+
`${candidates.length} slice report(s) follow.`,
|
|
85
|
+
'',
|
|
86
|
+
JSON.stringify(candidates, null, 2),
|
|
87
|
+
'',
|
|
88
|
+
'Merge into one JSON object.',
|
|
89
|
+
].join('\n');
|
|
90
|
+
}
|
|
91
|
+
/** Render messages for a model: role-tagged, tool calls named, results clamped. */
|
|
92
|
+
export function renderWindow(messages, maxToolResultChars = 600) {
|
|
93
|
+
return messages
|
|
94
|
+
.map((m) => {
|
|
95
|
+
if (m.role === 'tool') {
|
|
96
|
+
const body = m.content.length > maxToolResultChars
|
|
97
|
+
? `${m.content.slice(0, maxToolResultChars)} ... [${m.content.length - maxToolResultChars} chars elided]`
|
|
98
|
+
: m.content;
|
|
99
|
+
return `TOOL RESULT (${m.toolName ?? 'unknown'}): ${body}`;
|
|
100
|
+
}
|
|
101
|
+
if (m.toolName) {
|
|
102
|
+
const args = m.toolInput === undefined ? '' : ` ${JSON.stringify(m.toolInput).slice(0, 300)}`;
|
|
103
|
+
return `${m.role.toUpperCase()} CALLS ${m.toolName}:${args}`;
|
|
104
|
+
}
|
|
105
|
+
return `${m.role.toUpperCase()}: ${m.content}`;
|
|
106
|
+
})
|
|
107
|
+
.join('\n\n');
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC5B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IACnC,8EAA8E;IAC9E,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAC7C,SAAS,EAAE,CAAC;SACT,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;SAC3D,OAAO,CAAC,EAAE,CAAC;IACd,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACpF,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACtC,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IACzC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CACvC,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC;IACjD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC/B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;CAClD,CAAC,CAAC;AAUH,MAAM,SAAS,GAAG,2EAA2E,CAAC;AAE9F,MAAM,UAAU,mBAAmB;IACjC,OAAO;QACL,wFAAwF;QACxF,EAAE;QACF,gFAAgF;QAChF,iFAAiF;QACjF,sDAAsD;QACtD,EAAE;QACF,QAAQ;QACR,+EAA+E;QAC/E,2EAA2E;QAC3E,kEAAkE;QAClE,4EAA4E;QAC5E,qDAAqD;QACrD,iFAAiF;QACjF,4DAA4D;QAC5D,8CAA8C;QAC9C,EAAE;QACF,SAAS;QACT,+FAA+F;QAC/F,sFAAsF;KACvF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,UAAkB,EAAE,IAAgB;IACpE,OAAO;QACL,eAAe,IAAI,CAAC,KAAK,IAAI,YAAY,aAAa,IAAI,CAAC,MAAM,GAAG;QACpE,SAAS,IAAI,CAAC,KAAK,GAAG,CAAC,OAAO,IAAI,CAAC,KAAK,GAAG;QAC3C,EAAE;QACF,qBAAqB;QACrB,UAAU;QACV,mBAAmB;QACnB,EAAE;QACF,4BAA4B;KAC7B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,+EAA+E;QAC/E,EAAE;QACF,QAAQ;QACR,kFAAkF;QAClF,6EAA6E;QAC7E,+EAA+E;QAC/E,gFAAgF;QAChF,iEAAiE;QACjE,0EAA0E;QAC1E,iFAAiF;QACjF,uEAAuE;QACvE,EAAE;QACF,SAAS;QACT,oEAAoE;QACpE,kFAAkF;QAClF,+DAA+D;KAChE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,UAAuB,EAAE,IAAwC;IAC/F,OAAO;QACL,eAAe,IAAI,CAAC,KAAK,IAAI,YAAY,aAAa,IAAI,CAAC,MAAM,GAAG;QACpE,GAAG,UAAU,CAAC,MAAM,0BAA0B;QAC9C,EAAE;QACF,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QACnC,EAAE;QACF,6BAA6B;KAC9B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,YAAY,CAAC,QAA6B,EAAE,kBAAkB,GAAG,GAAG;IAClF,OAAO,QAAQ;SACZ,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,kBAAkB;gBAChD,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,kBAAkB,gBAAgB;gBACzG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YACd,OAAO,gBAAgB,CAAC,CAAC,QAAQ,IAAI,SAAS,MAAM,IAAI,EAAE,CAAC;QAC7D,CAAC;QACD,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;YACf,MAAM,IAAI,GAAG,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;YAC9F,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC;QAC/D,CAAC;QACD,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;IACjD,CAAC,CAAC;SACD,IAAI,CAAC,MAAM,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { DistilledAgent, Transcript } from '@husk-ai/core';
|
|
2
|
+
/**
|
|
3
|
+
* Everything that leaves this package -- to disk, to a model, to a log -- goes
|
|
4
|
+
* through here first. Transcripts are the single richest source of secrets a
|
|
5
|
+
* user owns: they contain whatever got pasted into a chat window.
|
|
6
|
+
*/
|
|
7
|
+
export type RedactionKind = 'secret' | 'home-path' | 'email';
|
|
8
|
+
export interface RedactionReport {
|
|
9
|
+
total: number;
|
|
10
|
+
counts: Record<RedactionKind, number>;
|
|
11
|
+
/** One line the CLI can print verbatim. Never contains a redacted value. */
|
|
12
|
+
summary: string;
|
|
13
|
+
}
|
|
14
|
+
export interface RedactOptions {
|
|
15
|
+
/** Replace the current user's home directory and any sibling home with `~`. */
|
|
16
|
+
homePaths?: boolean;
|
|
17
|
+
/** Replace email addresses with `[email]`. */
|
|
18
|
+
emails?: boolean;
|
|
19
|
+
/** Run `redact()` from @husk-ai/core over credential-shaped strings. */
|
|
20
|
+
secrets?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** Scrub one string. The report says what was removed, never what it was. */
|
|
23
|
+
export declare function redactText(input: string, opts?: RedactOptions): {
|
|
24
|
+
text: string;
|
|
25
|
+
report: RedactionReport;
|
|
26
|
+
};
|
|
27
|
+
export declare function emptyReport(): RedactionReport;
|
|
28
|
+
/** Scrub every string a transcript carries: title, origin, message bodies, tool input. */
|
|
29
|
+
export declare function redactTranscript(transcript: Transcript, opts?: RedactOptions): {
|
|
30
|
+
transcript: Transcript;
|
|
31
|
+
report: RedactionReport;
|
|
32
|
+
};
|
|
33
|
+
/** Scrub a distilled agent before it is written as a husk.yaml. */
|
|
34
|
+
export declare function redactDistilled(agent: DistilledAgent, opts?: RedactOptions): {
|
|
35
|
+
agent: DistilledAgent;
|
|
36
|
+
report: RedactionReport;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=redactor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redactor.d.ts","sourceRoot":"","sources":["../src/redactor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAqB,MAAM,eAAe,CAAC;AAEnF;;;;GAIG;AAEH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,WAAW,GAAG,OAAO,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IACtC,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,+EAA+E;IAC/E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAyDD,6EAA6E;AAC7E,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,aAAkB,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,CAiC3C;AAUD,wBAAgB,WAAW,IAAI,eAAe,CAE7C;AAED,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,UAAU,EACtB,IAAI,GAAE,aAAkB,GACvB;IAAE,UAAU,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,CAwBrD;AAED,mEAAmE;AACnE,wBAAgB,eAAe,CAC7B,KAAK,EAAE,cAAc,EACrB,IAAI,GAAE,aAAkB,GACvB;IAAE,KAAK,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,CAwBpD"}
|
package/dist/redactor.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { redact as redactSecrets } from '@husk-ai/core';
|
|
3
|
+
const DEFAULTS = { homePaths: true, emails: true, secrets: true };
|
|
4
|
+
/**
|
|
5
|
+
* Ordered longest-form-first so the escaped `C:\\Users\\x` form is consumed
|
|
6
|
+
* before the single-backslash pattern can eat half of it.
|
|
7
|
+
*/
|
|
8
|
+
const HOME_PATTERNS = [
|
|
9
|
+
/[A-Za-z]:\\\\Users\\\\[^\\"'\s:*?<>|]+/g,
|
|
10
|
+
/[A-Za-z]:\\Users\\[^\\/"'\s:*?<>|]+/g,
|
|
11
|
+
/[A-Za-z]:\/Users\/[^\\/"'\s:*?<>|]+/g,
|
|
12
|
+
/\/home\/[A-Za-z0-9._][A-Za-z0-9._-]*/g,
|
|
13
|
+
/\/Users\/[A-Za-z0-9._][A-Za-z0-9._-]*/g,
|
|
14
|
+
];
|
|
15
|
+
const EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?(?:\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,24}/g;
|
|
16
|
+
function emptyCounts() {
|
|
17
|
+
return { secret: 0, 'home-path': 0, email: 0 };
|
|
18
|
+
}
|
|
19
|
+
function escapeRegex(s) {
|
|
20
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
21
|
+
}
|
|
22
|
+
/** The literal home directory of the running user, in every spelling it appears in. */
|
|
23
|
+
function literalHomePatterns() {
|
|
24
|
+
const home = homedir();
|
|
25
|
+
if (!home)
|
|
26
|
+
return [];
|
|
27
|
+
const variants = new Set([home, home.replace(/\\/g, '/'), home.replace(/\\/g, '\\\\')]);
|
|
28
|
+
return [...variants].map((v) => new RegExp(escapeRegex(v), 'gi'));
|
|
29
|
+
}
|
|
30
|
+
function countingReplace(input, pattern, replacement) {
|
|
31
|
+
let hits = 0;
|
|
32
|
+
const text = input.replace(pattern, () => {
|
|
33
|
+
hits++;
|
|
34
|
+
return replacement;
|
|
35
|
+
});
|
|
36
|
+
return { text, hits };
|
|
37
|
+
}
|
|
38
|
+
function summarise(counts) {
|
|
39
|
+
const parts = [];
|
|
40
|
+
if (counts.secret)
|
|
41
|
+
parts.push(`${counts.secret} credential${counts.secret === 1 ? '' : 's'}`);
|
|
42
|
+
if (counts['home-path'])
|
|
43
|
+
parts.push(`${counts['home-path']} home path${counts['home-path'] === 1 ? '' : 's'}`);
|
|
44
|
+
if (counts.email)
|
|
45
|
+
parts.push(`${counts.email} email${counts.email === 1 ? '' : 's'}`);
|
|
46
|
+
if (!parts.length)
|
|
47
|
+
return 'nothing to redact';
|
|
48
|
+
return `redacted ${parts.join(', ')}`;
|
|
49
|
+
}
|
|
50
|
+
/** Scrub one string. The report says what was removed, never what it was. */
|
|
51
|
+
export function redactText(input, opts = {}) {
|
|
52
|
+
const o = { ...DEFAULTS, ...opts };
|
|
53
|
+
const counts = emptyCounts();
|
|
54
|
+
let text = input;
|
|
55
|
+
if (o.secrets) {
|
|
56
|
+
const before = text;
|
|
57
|
+
text = redactSecrets(text);
|
|
58
|
+
// core's redact() does not count, so infer from the marker it leaves behind.
|
|
59
|
+
if (text !== before) {
|
|
60
|
+
counts.secret =
|
|
61
|
+
(text.match(/\[redacted(?: private key)?\]/g) ?? []).length -
|
|
62
|
+
(before.match(/\[redacted(?: private key)?\]/g) ?? []).length;
|
|
63
|
+
if (counts.secret < 1)
|
|
64
|
+
counts.secret = 1;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (o.homePaths) {
|
|
68
|
+
for (const p of [...literalHomePatterns(), ...HOME_PATTERNS]) {
|
|
69
|
+
const r = countingReplace(text, p, '~');
|
|
70
|
+
text = r.text;
|
|
71
|
+
counts['home-path'] += r.hits;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (o.emails) {
|
|
75
|
+
const r = countingReplace(text, EMAIL, '[email]');
|
|
76
|
+
text = r.text;
|
|
77
|
+
counts.email += r.hits;
|
|
78
|
+
}
|
|
79
|
+
const total = counts.secret + counts['home-path'] + counts.email;
|
|
80
|
+
return { text, report: { total, counts, summary: summarise(counts) } };
|
|
81
|
+
}
|
|
82
|
+
function mergeInto(target, add) {
|
|
83
|
+
target.counts.secret += add.counts.secret;
|
|
84
|
+
target.counts['home-path'] += add.counts['home-path'];
|
|
85
|
+
target.counts.email += add.counts.email;
|
|
86
|
+
target.total = target.counts.secret + target.counts['home-path'] + target.counts.email;
|
|
87
|
+
target.summary = summarise(target.counts);
|
|
88
|
+
}
|
|
89
|
+
export function emptyReport() {
|
|
90
|
+
return { total: 0, counts: emptyCounts(), summary: summarise(emptyCounts()) };
|
|
91
|
+
}
|
|
92
|
+
/** Scrub every string a transcript carries: title, origin, message bodies, tool input. */
|
|
93
|
+
export function redactTranscript(transcript, opts = {}) {
|
|
94
|
+
const report = emptyReport();
|
|
95
|
+
const take = (s) => {
|
|
96
|
+
const r = redactText(s, opts);
|
|
97
|
+
mergeInto(report, r.report);
|
|
98
|
+
return r.text;
|
|
99
|
+
};
|
|
100
|
+
const messages = transcript.messages.map((m) => {
|
|
101
|
+
const next = { ...m, content: take(m.content) };
|
|
102
|
+
if (m.toolInput !== undefined) {
|
|
103
|
+
try {
|
|
104
|
+
next.toolInput = JSON.parse(take(JSON.stringify(m.toolInput)));
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
next.toolInput = '[unserialisable tool input]';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return next;
|
|
111
|
+
});
|
|
112
|
+
const out = { ...transcript, messages };
|
|
113
|
+
if (transcript.title)
|
|
114
|
+
out.title = take(transcript.title);
|
|
115
|
+
if (transcript.origin)
|
|
116
|
+
out.origin = take(transcript.origin);
|
|
117
|
+
return { transcript: out, report };
|
|
118
|
+
}
|
|
119
|
+
/** Scrub a distilled agent before it is written as a husk.yaml. */
|
|
120
|
+
export function redactDistilled(agent, opts = {}) {
|
|
121
|
+
const report = emptyReport();
|
|
122
|
+
const take = (s) => {
|
|
123
|
+
const r = redactText(s, opts);
|
|
124
|
+
mergeInto(report, r.report);
|
|
125
|
+
return r.text;
|
|
126
|
+
};
|
|
127
|
+
return {
|
|
128
|
+
agent: {
|
|
129
|
+
...agent,
|
|
130
|
+
name: take(agent.name),
|
|
131
|
+
description: take(agent.description),
|
|
132
|
+
persona: take(agent.persona),
|
|
133
|
+
knowledge: agent.knowledge.map((k) => ({
|
|
134
|
+
title: take(k.title),
|
|
135
|
+
content: take(k.content),
|
|
136
|
+
...(k.source ? { source: take(k.source) } : {}),
|
|
137
|
+
})),
|
|
138
|
+
examples: agent.examples.map((e) => ({ user: take(e.user), assistant: take(e.assistant) })),
|
|
139
|
+
notes: agent.notes.map(take),
|
|
140
|
+
},
|
|
141
|
+
report,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=redactor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redactor.js","sourceRoot":"","sources":["../src/redactor.ts"],"names":[],"mappings":"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"}
|