@interchained/portal-agent 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.
package/dist/guard.js ADDED
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Portal Guard — prevents bad AI output from entering the codebase.
3
+ *
4
+ * Runs a set of deterministic safety checks BEFORE any patch is applied.
5
+ * Unlike audit (which checks existing code), guard checks proposed patches.
6
+ *
7
+ * Checks:
8
+ * - No hallucinated phone numbers or emails (compares against data sources)
9
+ * - No changed brand colors without approval
10
+ * - No forbidden claims
11
+ * - No broken internal links (routes that don't exist)
12
+ * - No unsafe dependency additions
13
+ */
14
+ // ── Individual guard rules ─────────────────────────────────────────────────────
15
+ function checkForbiddenClaims(proposed, contract) {
16
+ const claims = contract.policies?.forbiddenClaims ?? [];
17
+ return claims
18
+ .filter((claim) => proposed.toLowerCase().includes(claim.toLowerCase()))
19
+ .map((claim) => ({
20
+ rule: "forbidden-claim",
21
+ severity: "block",
22
+ message: `Proposed patch contains forbidden claim: "${claim}"`,
23
+ evidence: claim,
24
+ }));
25
+ }
26
+ function checkForbiddenPhrases(proposed, contract) {
27
+ const phrases = contract.brand?.forbiddenPhrases ?? [];
28
+ return phrases
29
+ .filter((p) => proposed.toLowerCase().includes(p.toLowerCase()))
30
+ .map((p) => ({
31
+ rule: "forbidden-phrase",
32
+ severity: "block",
33
+ message: `Proposed patch contains forbidden brand phrase: "${p}"`,
34
+ evidence: p,
35
+ }));
36
+ }
37
+ function checkBrandColors(original, proposed, contract) {
38
+ const colors = contract.brand?.colors ?? [];
39
+ const violations = [];
40
+ for (const color of colors) {
41
+ const inOriginal = original.includes(color);
42
+ const inProposed = proposed.includes(color);
43
+ if (inOriginal && !inProposed) {
44
+ violations.push({
45
+ rule: "brand-color-removed",
46
+ severity: "warn",
47
+ message: `Brand color ${color} was present in original but removed in patch`,
48
+ evidence: color,
49
+ });
50
+ }
51
+ }
52
+ // Check for new hex colors not in the brand palette
53
+ const hexPattern = /#([0-9a-fA-F]{3,8})\b/g;
54
+ const originalColors = new Set([...original.matchAll(hexPattern)].map((m) => m[0].toLowerCase()));
55
+ const proposedColors = [...proposed.matchAll(hexPattern)].map((m) => m[0].toLowerCase());
56
+ const brandPalette = new Set(colors.map((c) => c.toLowerCase()));
57
+ for (const hex of proposedColors) {
58
+ if (!originalColors.has(hex) && !brandPalette.has(hex) && colors.length > 0) {
59
+ violations.push({
60
+ rule: "off-brand-color",
61
+ severity: "warn",
62
+ message: `New color ${hex} introduced — not in brand palette (${colors.join(", ")})`,
63
+ evidence: hex,
64
+ });
65
+ }
66
+ }
67
+ return violations;
68
+ }
69
+ function checkPhoneNumbers(original, proposed) {
70
+ const phonePattern = /\b(\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g;
71
+ const originalPhones = new Set([...original.matchAll(phonePattern)].map((m) => m[0]));
72
+ const proposedPhones = [...proposed.matchAll(phonePattern)];
73
+ const violations = [];
74
+ for (const match of proposedPhones) {
75
+ if (!originalPhones.has(match[0])) {
76
+ violations.push({
77
+ rule: "hallucinated-phone",
78
+ severity: "block",
79
+ message: `New phone number "${match[0]}" found in patch — verify this is from a trusted data source`,
80
+ evidence: match[0],
81
+ });
82
+ }
83
+ }
84
+ return violations;
85
+ }
86
+ // ── Main guard runner ─────────────────────────────────────────────────────────
87
+ export async function guardPatch(patch, contract) {
88
+ const violations = [
89
+ ...checkForbiddenClaims(patch.proposed, contract),
90
+ ...checkForbiddenPhrases(patch.proposed, contract),
91
+ ...checkBrandColors(patch.original, patch.proposed, contract),
92
+ ...checkPhoneNumbers(patch.original, patch.proposed),
93
+ ];
94
+ const blocking = violations.filter((v) => v.severity === "block");
95
+ return {
96
+ patchId: patch.id,
97
+ passed: blocking.length === 0,
98
+ violations,
99
+ };
100
+ }
101
+ /** Run guard checks across all pending patches */
102
+ export async function guardAllPending(patches, contract) {
103
+ const pending = patches.filter((p) => p.status === "pending");
104
+ return Promise.all(pending.map((p) => guardPatch(p, contract)));
105
+ }
106
+ //# sourceMappingURL=guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Improvement engine.
3
+ *
4
+ * Takes audit findings and generates targeted patches to fix them.
5
+ * Each patch is passed through the Sentinel, then queued for approval.
6
+ */
7
+ import type { AppContract } from "@interchained/portal-contract";
8
+ import { Runner } from "./runner.js";
9
+ import { Sentinel } from "./sentinel.js";
10
+ import { type Patch } from "./patch.js";
11
+ import type { AuditFinding, CheckCategory } from "./audit.js";
12
+ export interface ImproveOptions {
13
+ runner: Runner;
14
+ sentinel?: Sentinel;
15
+ contract: AppContract;
16
+ projectRoot: string;
17
+ /** Filter to specific check categories */
18
+ target?: CheckCategory[];
19
+ requiresApproval?: boolean;
20
+ }
21
+ export interface ImproveResult {
22
+ finding: AuditFinding;
23
+ patch: Patch | null;
24
+ skipped?: string;
25
+ sentinelReview?: {
26
+ approved: boolean;
27
+ summary: string;
28
+ violations: string[];
29
+ };
30
+ }
31
+ /**
32
+ * Generate improvement patches for a list of audit findings.
33
+ * Returns one result per finding — some may be skipped if they need
34
+ * manual intervention.
35
+ */
36
+ export declare function improveFromFindings(findings: AuditFinding[], opts: ImproveOptions): Promise<ImproveResult[]>;
37
+ //# sourceMappingURL=improve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"improve.d.ts","sourceRoot":"","sources":["../src/improve.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE9D,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,EAAE,WAAW,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;IACzB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE;QACf,QAAQ,EAAE,OAAO,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC;CACH;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,YAAY,EAAE,EACxB,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,aAAa,EAAE,CAAC,CA2E1B"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Improvement engine.
3
+ *
4
+ * Takes audit findings and generates targeted patches to fix them.
5
+ * Each patch is passed through the Sentinel, then queued for approval.
6
+ */
7
+ import { readFile } from "node:fs/promises";
8
+ import { join } from "node:path";
9
+ import { createPatch } from "./patch.js";
10
+ /**
11
+ * Generate improvement patches for a list of audit findings.
12
+ * Returns one result per finding — some may be skipped if they need
13
+ * manual intervention.
14
+ */
15
+ export async function improveFromFindings(findings, opts) {
16
+ const { runner, sentinel, contract, projectRoot } = opts;
17
+ const targets = findings.filter((f) => {
18
+ if (f.status === "pass")
19
+ return false;
20
+ if (opts.target && !opts.target.includes(f.category))
21
+ return false;
22
+ return true;
23
+ });
24
+ const requiresApproval = opts.requiresApproval ??
25
+ contract.policies?.publishing === "human_review";
26
+ const results = [];
27
+ for (const finding of targets) {
28
+ let original;
29
+ try {
30
+ original = await readFile(join(projectRoot, finding.file), "utf-8");
31
+ }
32
+ catch {
33
+ results.push({ finding, patch: null, skipped: "File not found" });
34
+ continue;
35
+ }
36
+ const contractSummary = `Name: ${contract.name}\nGoals: ${contract.goals.join("\n")}\nPolicies: ${JSON.stringify(contract.policies ?? {})}`;
37
+ let proposed;
38
+ try {
39
+ proposed = await runner.generatePatch({
40
+ filePath: finding.file,
41
+ originalContent: original,
42
+ finding: `[${finding.category.toUpperCase()}] ${finding.message}${finding.suggestion ? ` — Suggested fix: ${finding.suggestion}` : ""}`,
43
+ contract: contractSummary,
44
+ });
45
+ }
46
+ catch {
47
+ results.push({ finding, patch: null, skipped: "Runner failed to generate patch" });
48
+ continue;
49
+ }
50
+ let sentinelResult;
51
+ if (sentinel) {
52
+ try {
53
+ const review = await sentinel.review({
54
+ contract,
55
+ filePath: finding.file,
56
+ original,
57
+ proposed,
58
+ agentTask: `Fix ${finding.category} issue: ${finding.message}`,
59
+ });
60
+ sentinelResult = {
61
+ approved: review.approved,
62
+ summary: review.summary,
63
+ violations: review.violations,
64
+ };
65
+ }
66
+ catch {
67
+ // Sentinel failure doesn't block — patch is just flagged
68
+ }
69
+ }
70
+ const patch = createPatch({
71
+ agent: "portal-improve",
72
+ file: finding.file,
73
+ original,
74
+ proposed,
75
+ reason: `Fix [${finding.category}]: ${finding.message}`,
76
+ requiresApproval,
77
+ sentinelApproved: sentinelResult?.approved,
78
+ sentinelSummary: sentinelResult?.summary,
79
+ sentinelViolations: sentinelResult?.violations,
80
+ });
81
+ results.push({ finding, patch, sentinelReview: sentinelResult });
82
+ }
83
+ return results;
84
+ }
85
+ //# sourceMappingURL=improve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"improve.js","sourceRoot":"","sources":["../src/improve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export { AiAssistClient, AiAssistError } from "./aiassist.js";
2
+ export type { AiAssistConfig, Message, CompletionOptions } from "./aiassist.js";
3
+ export { Runner } from "./runner.js";
4
+ export type { RunnerConfig } from "./runner.js";
5
+ export { Sentinel } from "./sentinel.js";
6
+ export type { SentinelReview } from "./sentinel.js";
7
+ export { runAudit } from "./audit.js";
8
+ export type { AuditFinding, AuditReport, CheckCategory, CheckStatus } from "./audit.js";
9
+ export { generatePage, generateFromPrompt } from "./generate.js";
10
+ export type { GenerateOptions, GenerateResult } from "./generate.js";
11
+ export { improveFromFindings } from "./improve.js";
12
+ export type { ImproveOptions, ImproveResult } from "./improve.js";
13
+ export { guardPatch, guardAllPending } from "./guard.js";
14
+ export type { GuardViolation, GuardResult } from "./guard.js";
15
+ export { createPatch, applyPatch, inlineDiff, PatchStore, } from "./patch.js";
16
+ export type { Patch, PatchStatus } from "./patch.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,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9D,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEhF,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAExF,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACjE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACzD,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9D,OAAO,EACL,WAAW,EACX,UAAU,EACV,UAAU,EACV,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export { AiAssistClient, AiAssistError } from "./aiassist.js";
2
+ export { Runner } from "./runner.js";
3
+ export { Sentinel } from "./sentinel.js";
4
+ export { runAudit } from "./audit.js";
5
+ export { generatePage, generateFromPrompt } from "./generate.js";
6
+ export { improveFromFindings } from "./improve.js";
7
+ export { guardPatch, guardAllPending } from "./guard.js";
8
+ export { createPatch, applyPatch, inlineDiff, PatchStore, } from "./patch.js";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGnD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGzD,OAAO,EACL,WAAW,EACX,UAAU,EACV,UAAU,EACV,UAAU,GACX,MAAM,YAAY,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Safe patch system — preview, approval, and application.
3
+ *
4
+ * Every agent-generated change is represented as a Patch.
5
+ * Patches with requiresApproval=true are shown as diffs
6
+ * and must be explicitly approved before being written to disk.
7
+ */
8
+ export type PatchStatus = "pending" | "approved" | "rejected" | "applied";
9
+ export interface Patch {
10
+ id: string;
11
+ /** Agent that generated this patch */
12
+ agent: string;
13
+ /** Path relative to project root */
14
+ file: string;
15
+ original: string;
16
+ proposed: string;
17
+ /** Human-readable reason for the change */
18
+ reason: string;
19
+ requiresApproval: boolean;
20
+ status: PatchStatus;
21
+ createdAt: string;
22
+ /** Sentinel review (if sentinel ran) */
23
+ sentinelApproved?: boolean;
24
+ sentinelSummary?: string;
25
+ sentinelViolations?: string[];
26
+ }
27
+ export declare class PatchStore {
28
+ private dir;
29
+ constructor(projectRoot: string);
30
+ save(patch: Patch): Promise<void>;
31
+ load(id: string): Promise<Patch | null>;
32
+ list(): Promise<Patch[]>;
33
+ update(id: string, updates: Partial<Patch>): Promise<void>;
34
+ }
35
+ export declare function createPatch(opts: Omit<Patch, "id" | "createdAt" | "status">): Patch;
36
+ export declare function applyPatch(patch: Patch, projectRoot: string, store: PatchStore): Promise<void>;
37
+ export declare function inlineDiff(original: string, proposed: string): string;
38
+ //# sourceMappingURL=patch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;AAE1E,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,OAAO,CAAC;IAC1B,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAID,qBAAa,UAAU;IACrB,OAAO,CAAC,GAAG,CAAS;gBAER,WAAW,EAAE,MAAM;IAIzB,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IASjC,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IASvC,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;IAmBxB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAKjE;AAID,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,WAAW,GAAG,QAAQ,CAAC,GAAG,KAAK,CAOnF;AAID,wBAAsB,UAAU,CAC9B,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,IAAI,CAAC,CAQf;AAID,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAiBrE"}
package/dist/patch.js ADDED
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Safe patch system — preview, approval, and application.
3
+ *
4
+ * Every agent-generated change is represented as a Patch.
5
+ * Patches with requiresApproval=true are shown as diffs
6
+ * and must be explicitly approved before being written to disk.
7
+ */
8
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
9
+ import { dirname, join } from "node:path";
10
+ import { randomUUID } from "node:crypto";
11
+ // ── Patch store (file-backed, .portal/patches/) ───────────────────────────────
12
+ export class PatchStore {
13
+ dir;
14
+ constructor(projectRoot) {
15
+ this.dir = join(projectRoot, ".portal", "patches");
16
+ }
17
+ async save(patch) {
18
+ await mkdir(this.dir, { recursive: true });
19
+ await writeFile(join(this.dir, `${patch.id}.json`), JSON.stringify(patch, null, 2), "utf-8");
20
+ }
21
+ async load(id) {
22
+ try {
23
+ const raw = await readFile(join(this.dir, `${id}.json`), "utf-8");
24
+ return JSON.parse(raw);
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ }
30
+ async list() {
31
+ const { readdir } = await import("node:fs/promises");
32
+ try {
33
+ const files = await readdir(this.dir);
34
+ const patches = [];
35
+ for (const f of files) {
36
+ if (f.endsWith(".json")) {
37
+ try {
38
+ const raw = await readFile(join(this.dir, f), "utf-8");
39
+ patches.push(JSON.parse(raw));
40
+ }
41
+ catch { /* skip corrupt */ }
42
+ }
43
+ }
44
+ return patches.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
45
+ }
46
+ catch {
47
+ return [];
48
+ }
49
+ }
50
+ async update(id, updates) {
51
+ const patch = await this.load(id);
52
+ if (!patch)
53
+ throw new Error(`Patch ${id} not found`);
54
+ await this.save({ ...patch, ...updates });
55
+ }
56
+ }
57
+ // ── Patch factory ─────────────────────────────────────────────────────────────
58
+ export function createPatch(opts) {
59
+ return {
60
+ ...opts,
61
+ id: randomUUID(),
62
+ status: "pending",
63
+ createdAt: new Date().toISOString(),
64
+ };
65
+ }
66
+ // ── Apply ─────────────────────────────────────────────────────────────────────
67
+ export async function applyPatch(patch, projectRoot, store) {
68
+ if (patch.status !== "approved" && patch.requiresApproval) {
69
+ throw new Error(`Patch ${patch.id} requires approval before applying`);
70
+ }
71
+ const filePath = join(projectRoot, patch.file);
72
+ await mkdir(dirname(filePath), { recursive: true });
73
+ await writeFile(filePath, patch.proposed, "utf-8");
74
+ await store.update(patch.id, { status: "applied" });
75
+ }
76
+ // ── Inline diff (terminal-friendly) ──────────────────────────────────────────
77
+ export function inlineDiff(original, proposed) {
78
+ const oLines = original.split("\n");
79
+ const pLines = proposed.split("\n");
80
+ const lines = [];
81
+ const maxLen = Math.max(oLines.length, pLines.length);
82
+ for (let i = 0; i < maxLen; i++) {
83
+ const o = oLines[i];
84
+ const p = pLines[i];
85
+ if (o === p) {
86
+ lines.push(` ${o ?? ""}`);
87
+ }
88
+ else {
89
+ if (o !== undefined)
90
+ lines.push(`- ${o}`);
91
+ if (p !== undefined)
92
+ lines.push(`+ ${p}`);
93
+ }
94
+ }
95
+ return lines.join("\n");
96
+ }
97
+ //# sourceMappingURL=patch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.js","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Runner — fast, cheap first-pass AI model.
3
+ * Does the actual generation/analysis work.
4
+ * Output is reviewed by the Sentinel before being applied.
5
+ */
6
+ import { type AiAssistConfig } from "./aiassist.js";
7
+ export interface RunnerConfig extends Partial<AiAssistConfig> {
8
+ /** Override model with a fast/cheap option */
9
+ model?: string;
10
+ }
11
+ export declare class Runner {
12
+ private client;
13
+ constructor(config?: RunnerConfig);
14
+ run(task: string, context: string, systemPrompt?: string): Promise<string>;
15
+ /** Generate a React page component */
16
+ generateComponent(opts: {
17
+ route: string;
18
+ purpose: string;
19
+ audience?: string;
20
+ primaryAction?: string;
21
+ brandVoice?: string;
22
+ colors?: string[];
23
+ existingImports?: string;
24
+ }): Promise<string>;
25
+ /** Analyze a file for audit issues */
26
+ analyzeFile(opts: {
27
+ filePath: string;
28
+ content: string;
29
+ checks: string[];
30
+ contract: string;
31
+ }): Promise<string>;
32
+ /** Generate an improvement patch for an audit finding */
33
+ generatePatch(opts: {
34
+ filePath: string;
35
+ originalContent: string;
36
+ finding: string;
37
+ contract: string;
38
+ }): Promise<string>;
39
+ }
40
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAkB,KAAK,cAAc,EAAgB,MAAM,eAAe,CAAC;AAElF,MAAM,WAAW,YAAa,SAAQ,OAAO,CAAC,cAAc,CAAC;IAC3D,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAiB;gBAEnB,MAAM,GAAE,YAAiB;IAS/B,GAAG,CACP,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC;IAYlB,sCAAsC;IAChC,iBAAiB,CAAC,IAAI,EAAE;QAC5B,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,GAAG,OAAO,CAAC,MAAM,CAAC;IAmBnB,sCAAsC;IAChC,WAAW,CAAC,IAAI,EAAE;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBnB,yDAAyD;IACnD,aAAa,CAAC,IAAI,EAAE;QACxB,QAAQ,EAAE,MAAM,CAAC;QACjB,eAAe,EAAE,MAAM,CAAC;QACxB,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,MAAM,CAAC;CAoBpB"}
package/dist/runner.js ADDED
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Runner — fast, cheap first-pass AI model.
3
+ * Does the actual generation/analysis work.
4
+ * Output is reviewed by the Sentinel before being applied.
5
+ */
6
+ import { AiAssistClient } from "./aiassist.js";
7
+ export class Runner {
8
+ client;
9
+ constructor(config = {}) {
10
+ this.client = new AiAssistClient({
11
+ apiKey: config.apiKey ?? process.env["AIASSIST_API_KEY"] ?? process.env["VITE_AIAS_API_KEY"] ?? "",
12
+ baseUrl: config.baseUrl,
13
+ model: config.model ?? "gpt-4o-mini",
14
+ timeoutMs: config.timeoutMs ?? 45_000,
15
+ });
16
+ }
17
+ async run(task, context, systemPrompt) {
18
+ const messages = [];
19
+ if (systemPrompt) {
20
+ messages.push({ role: "system", content: systemPrompt });
21
+ }
22
+ messages.push({
23
+ role: "user",
24
+ content: `${context}\n\n---\nTASK: ${task}`,
25
+ });
26
+ return this.client.chat(messages, { temperature: 0.2 });
27
+ }
28
+ /** Generate a React page component */
29
+ async generateComponent(opts) {
30
+ const systemPrompt = `You are an expert React/TypeScript developer writing components for the Portal framework.
31
+ Output ONLY valid TypeScript JSX. No markdown, no explanation, no code fences.
32
+ Use Tailwind CSS for styling. Follow these rules:
33
+ - Use functional components with explicit return types
34
+ - Import from "@interchained/portal-react" for Link, Head, useNavigate, useParams
35
+ - Colors: ${opts.colors?.join(", ") ?? "follow modern design"}
36
+ - Voice/tone: ${opts.brandVoice ?? "professional and clear"}
37
+ - Always export default`;
38
+ const prompt = `Generate a React page component for the route "${opts.route}".
39
+ Purpose: ${opts.purpose}
40
+ ${opts.audience ? `Audience: ${opts.audience}` : ""}
41
+ ${opts.primaryAction ? `Primary action: ${opts.primaryAction}` : ""}
42
+ The component should directly serve the stated purpose with real, compelling content.`;
43
+ return this.client.complete(prompt, systemPrompt, { maxTokens: 3_000 });
44
+ }
45
+ /** Analyze a file for audit issues */
46
+ async analyzeFile(opts) {
47
+ const prompt = `Analyze this file for the following issues: ${opts.checks.join(", ")}.
48
+
49
+ App contract context:
50
+ ${opts.contract}
51
+
52
+ File: ${opts.filePath}
53
+ \`\`\`tsx
54
+ ${opts.content}
55
+ \`\`\`
56
+
57
+ Respond as JSON array of findings:
58
+ [{"check": "seo|cta|links|brand|accessibility", "status": "pass|warn|fail", "message": "...", "line": null, "suggestion": "..."}]`;
59
+ return this.client.complete(prompt, undefined, { temperature: 0.1 });
60
+ }
61
+ /** Generate an improvement patch for an audit finding */
62
+ async generatePatch(opts) {
63
+ const systemPrompt = `You are generating a minimal, surgical patch to fix a specific issue in a React/TypeScript file.
64
+ Output ONLY the complete corrected file contents — no explanation, no markdown.`;
65
+ const prompt = `Fix the following issue in this file:
66
+
67
+ Issue: ${opts.finding}
68
+
69
+ App contract:
70
+ ${opts.contract}
71
+
72
+ File: ${opts.filePath}
73
+ \`\`\`tsx
74
+ ${opts.originalContent}
75
+ \`\`\`
76
+
77
+ Return the complete corrected file. Change only what is necessary to fix the issue.`;
78
+ return this.client.complete(prompt, systemPrompt, { temperature: 0.1, maxTokens: 4_000 });
79
+ }
80
+ }
81
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAqC,MAAM,eAAe,CAAC;AAOlF,MAAM,OAAO,MAAM;IACT,MAAM,CAAiB;IAE/B,YAAY,SAAuB,EAAE;QACnC,IAAI,CAAC,MAAM,GAAG,IAAI,cAAc,CAAC;YAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,EAAE;YAClG,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,aAAa;YACpC,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,MAAM;SACtC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,GAAG,CACP,IAAY,EACZ,OAAe,EACf,YAAqB;QAErB,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,IAAI,YAAY,EAAE,CAAC;YACjB,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,GAAG,OAAO,kBAAkB,IAAI,EAAE;SAC5C,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,sCAAsC;IACtC,KAAK,CAAC,iBAAiB,CAAC,IAQvB;QACC,MAAM,YAAY,GAAG;;;;;YAKb,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,sBAAsB;gBAC7C,IAAI,CAAC,UAAU,IAAI,wBAAwB;wBACnC,CAAC;QAErB,MAAM,MAAM,GAAG,kDAAkD,IAAI,CAAC,KAAK;WACpE,IAAI,CAAC,OAAO;EACrB,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE;EACjD,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,mBAAmB,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE;sFACmB,CAAC;QAEnF,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,sCAAsC;IACtC,KAAK,CAAC,WAAW,CAAC,IAKjB;QACC,MAAM,MAAM,GAAG,+CAA+C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;;;EAGtF,IAAI,CAAC,QAAQ;;QAEP,IAAI,CAAC,QAAQ;;EAEnB,IAAI,CAAC,OAAO;;;;kIAIoH,CAAC;QAE/H,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,yDAAyD;IACzD,KAAK,CAAC,aAAa,CAAC,IAKnB;QACC,MAAM,YAAY,GAAG;gFACuD,CAAC;QAE7E,MAAM,MAAM,GAAG;;SAEV,IAAI,CAAC,OAAO;;;EAGnB,IAAI,CAAC,QAAQ;;QAEP,IAAI,CAAC,QAAQ;;EAEnB,IAAI,CAAC,eAAe;;;oFAG8D,CAAC;QAEjF,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5F,CAAC;CACF"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Sentinel — reviews Runner output before it touches the codebase.
3
+ *
4
+ * The sentinel is a stronger/slower model that:
5
+ * 1. Checks that the patch actually fixes what it claims to fix
6
+ * 2. Verifies no forbidden claims or brand violations were introduced
7
+ * 3. Confirms the change is faithful to the app contract
8
+ * 4. Produces a human-readable review summary for the approval UI
9
+ */
10
+ import { type AiAssistConfig } from "./aiassist.js";
11
+ import type { AppContract } from "@interchained/portal-contract";
12
+ export interface SentinelReview {
13
+ approved: boolean;
14
+ summary: string;
15
+ violations: string[];
16
+ /** Diff-friendly line annotations */
17
+ annotations: Array<{
18
+ line?: number;
19
+ type: "ok" | "warning" | "violation";
20
+ note: string;
21
+ }>;
22
+ }
23
+ export declare class Sentinel {
24
+ private client;
25
+ constructor(config?: Partial<AiAssistConfig>);
26
+ /** Review a generated patch against the app contract */
27
+ review(opts: {
28
+ contract: AppContract;
29
+ filePath: string;
30
+ original: string;
31
+ proposed: string;
32
+ agentTask: string;
33
+ }): Promise<SentinelReview>;
34
+ }
35
+ //# sourceMappingURL=sentinel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentinel.d.ts","sourceRoot":"","sources":["../src/sentinel.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,qCAAqC;IACrC,WAAW,EAAE,KAAK,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,IAAI,GAAG,SAAS,GAAG,WAAW,CAAC;QACrC,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;CACJ;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAiB;gBAEnB,MAAM,GAAE,OAAO,CAAC,cAAc,CAAM;IAShD,wDAAwD;IAClD,MAAM,CAAC,IAAI,EAAE;QACjB,QAAQ,EAAE,WAAW,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,cAAc,CAAC;CAsD5B"}