@interchained/portal-agent 0.1.1 → 0.1.2
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/generate.d.ts +21 -17
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +32 -32
- package/dist/generate.js.map +1 -1
- package/dist/sentinel.d.ts +32 -7
- package/dist/sentinel.d.ts.map +1 -1
- package/dist/sentinel.js +57 -11
- package/dist/sentinel.js.map +1 -1
- package/package.json +11 -10
package/dist/generate.d.ts
CHANGED
|
@@ -1,41 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Page / component generation.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Flow when Sentinel is present:
|
|
5
|
+
* Runner generates → Sentinel.reviewAndApply() → applied or blocked
|
|
6
|
+
*
|
|
7
|
+
* Flow without Sentinel (--no-sentinel or API key absent):
|
|
8
|
+
* Runner generates → patch saved as "pending" → human approves via CLI
|
|
7
9
|
*/
|
|
8
10
|
import type { AppContract, PageContract } from "@interchained/portal-contract";
|
|
9
11
|
import { Runner } from "./runner.js";
|
|
10
|
-
import { Sentinel } from "./sentinel.js";
|
|
11
|
-
import { type Patch } from "./patch.js";
|
|
12
|
+
import { Sentinel, type SentinelReview } from "./sentinel.js";
|
|
13
|
+
import { PatchStore, type Patch } from "./patch.js";
|
|
12
14
|
export interface GenerateOptions {
|
|
13
15
|
runner: Runner;
|
|
14
16
|
sentinel?: Sentinel;
|
|
15
17
|
contract: AppContract;
|
|
16
18
|
projectRoot: string;
|
|
17
|
-
|
|
19
|
+
route?: string;
|
|
20
|
+
/** Only consulted when NO sentinel is present */
|
|
18
21
|
requiresApproval?: boolean;
|
|
19
22
|
}
|
|
20
23
|
export interface GenerateResult {
|
|
21
24
|
patch: Patch;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
/** Set whenever a Sentinel ran (approved or not) */
|
|
26
|
+
sentinelReview?: SentinelReview;
|
|
27
|
+
/**
|
|
28
|
+
* True when the Sentinel approved and applied the patch to disk.
|
|
29
|
+
* False when sentinel rejected, or no sentinel ran.
|
|
30
|
+
* The CLI uses this to skip / show the human approval prompt.
|
|
31
|
+
*/
|
|
32
|
+
sentinelApplied: boolean;
|
|
27
33
|
}
|
|
28
34
|
/**
|
|
29
|
-
* Generate a new page component from a PageContract
|
|
35
|
+
* Generate a new page component from a PageContract.
|
|
30
36
|
*/
|
|
31
37
|
export declare function generatePage(page: PageContract & {
|
|
32
38
|
description?: string;
|
|
33
|
-
}, opts: GenerateOptions): Promise<GenerateResult>;
|
|
39
|
+
}, opts: GenerateOptions, store: PatchStore): Promise<GenerateResult>;
|
|
34
40
|
/**
|
|
35
|
-
* Generate a page from a freeform description
|
|
41
|
+
* Generate a page from a freeform description.
|
|
36
42
|
* `portal generate page "Father's Day promo"` style.
|
|
37
43
|
*/
|
|
38
|
-
export declare function generateFromPrompt(prompt: string, opts: GenerateOptions
|
|
39
|
-
route?: string;
|
|
40
|
-
}): Promise<GenerateResult>;
|
|
44
|
+
export declare function generateFromPrompt(prompt: string, opts: GenerateOptions, store?: PatchStore): Promise<GenerateResult>;
|
|
41
45
|
//# sourceMappingURL=generate.d.ts.map
|
package/dist/generate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAe,UAAU,EAAE,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,EAAE,WAAW,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,KAAK,CAAC;IACb,oDAAoD;IACpD,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC;;;;OAIG;IACH,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,YAAY,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EAC7C,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,cAAc,CAAC,CAsDzB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,eAAe,EACrB,KAAK,CAAC,EAAE,UAAU,GACjB,OAAO,CAAC,cAAc,CAAC,CAUzB"}
|
package/dist/generate.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Page / component generation.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Flow when Sentinel is present:
|
|
5
|
+
* Runner generates → Sentinel.reviewAndApply() → applied or blocked
|
|
6
|
+
*
|
|
7
|
+
* Flow without Sentinel (--no-sentinel or API key absent):
|
|
8
|
+
* Runner generates → patch saved as "pending" → human approves via CLI
|
|
7
9
|
*/
|
|
8
10
|
import { readFile } from "node:fs/promises";
|
|
9
11
|
import { join } from "node:path";
|
|
10
|
-
import { createPatch } from "./patch.js";
|
|
12
|
+
import { createPatch, PatchStore } from "./patch.js";
|
|
11
13
|
/**
|
|
12
|
-
* Generate a new page component from a PageContract
|
|
14
|
+
* Generate a new page component from a PageContract.
|
|
13
15
|
*/
|
|
14
|
-
export async function generatePage(page, opts) {
|
|
16
|
+
export async function generatePage(page, opts, store) {
|
|
15
17
|
const { runner, sentinel, contract } = opts;
|
|
16
|
-
// Check if the file already exists — use as context
|
|
17
18
|
const fileName = routeToFileName(page.route);
|
|
18
19
|
const filePath = join(opts.projectRoot, "routes", fileName);
|
|
19
20
|
let original = "";
|
|
@@ -21,7 +22,7 @@ export async function generatePage(page, opts) {
|
|
|
21
22
|
original = await readFile(filePath, "utf-8");
|
|
22
23
|
}
|
|
23
24
|
catch {
|
|
24
|
-
// New file
|
|
25
|
+
// New file
|
|
25
26
|
}
|
|
26
27
|
const proposed = await runner.generateComponent({
|
|
27
28
|
route: page.route,
|
|
@@ -31,23 +32,7 @@ export async function generatePage(page, opts) {
|
|
|
31
32
|
brandVoice: contract.brand?.voice,
|
|
32
33
|
colors: contract.brand?.colors,
|
|
33
34
|
});
|
|
34
|
-
const requiresApproval = opts.requiresApproval ??
|
|
35
|
-
contract.policies?.publishing === "human_review";
|
|
36
|
-
let sentinelResult;
|
|
37
|
-
if (sentinel) {
|
|
38
|
-
const review = await sentinel.review({
|
|
39
|
-
contract,
|
|
40
|
-
filePath,
|
|
41
|
-
original,
|
|
42
|
-
proposed,
|
|
43
|
-
agentTask: `Generate page for route "${page.route}": ${page.purpose}`,
|
|
44
|
-
});
|
|
45
|
-
sentinelResult = {
|
|
46
|
-
approved: review.approved,
|
|
47
|
-
summary: review.summary,
|
|
48
|
-
violations: review.violations,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
35
|
+
const requiresApproval = opts.requiresApproval ?? contract.policies?.publishing === "human_review";
|
|
51
36
|
const patch = createPatch({
|
|
52
37
|
agent: "portal-generate",
|
|
53
38
|
file: join("routes", fileName),
|
|
@@ -55,17 +40,32 @@ export async function generatePage(page, opts) {
|
|
|
55
40
|
proposed,
|
|
56
41
|
reason: `Generate page: ${page.purpose}`,
|
|
57
42
|
requiresApproval,
|
|
58
|
-
sentinelApproved: sentinelResult?.approved,
|
|
59
|
-
sentinelSummary: sentinelResult?.summary,
|
|
60
|
-
sentinelViolations: sentinelResult?.violations,
|
|
61
43
|
});
|
|
62
|
-
|
|
44
|
+
await store.save(patch);
|
|
45
|
+
// ── Sentinel is the gate + applier ─────────────────────────────────────────
|
|
46
|
+
if (sentinel) {
|
|
47
|
+
const result = await sentinel.reviewAndApply({
|
|
48
|
+
patch,
|
|
49
|
+
projectRoot: opts.projectRoot,
|
|
50
|
+
store,
|
|
51
|
+
contract,
|
|
52
|
+
agentTask: `Generate page for route "${page.route}": ${page.purpose}`,
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
patch,
|
|
56
|
+
sentinelReview: result.review,
|
|
57
|
+
sentinelApplied: result.applied,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// ── No sentinel — patch stays "pending", human decides ────────────────────
|
|
61
|
+
return { patch, sentinelApplied: false };
|
|
63
62
|
}
|
|
64
63
|
/**
|
|
65
|
-
* Generate a page from a freeform description
|
|
64
|
+
* Generate a page from a freeform description.
|
|
66
65
|
* `portal generate page "Father's Day promo"` style.
|
|
67
66
|
*/
|
|
68
|
-
export async function generateFromPrompt(prompt, opts) {
|
|
67
|
+
export async function generateFromPrompt(prompt, opts, store) {
|
|
68
|
+
const resolvedStore = store ?? new PatchStore(opts.projectRoot);
|
|
69
69
|
const route = opts.route ?? promptToRoute(prompt);
|
|
70
70
|
const page = {
|
|
71
71
|
route,
|
|
@@ -73,7 +73,7 @@ export async function generateFromPrompt(prompt, opts) {
|
|
|
73
73
|
audience: undefined,
|
|
74
74
|
primaryAction: undefined,
|
|
75
75
|
};
|
|
76
|
-
return generatePage(page, opts);
|
|
76
|
+
return generatePage(page, opts, resolvedStore);
|
|
77
77
|
}
|
|
78
78
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
79
79
|
function routeToFileName(route) {
|
package/dist/generate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"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"}
|
package/dist/sentinel.d.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Sentinel — reviews Runner output
|
|
2
|
+
* Sentinel — reviews Runner output and, if approved, applies the patch itself.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* The Sentinel is both the gate and the final patch auditor:
|
|
5
|
+
* - APPROVED → Sentinel writes the file, marks the patch "applied"
|
|
6
|
+
* - REJECTED → Sentinel blocks, surfaces violations, human must override
|
|
7
|
+
*
|
|
8
|
+
* Uses a stronger/slower model than the Runner so it can catch what the
|
|
9
|
+
* runner missed: brand violations, hallucinated data, forbidden claims,
|
|
10
|
+
* regressions, and contract drift.
|
|
9
11
|
*/
|
|
10
12
|
import { type AiAssistConfig } from "./aiassist.js";
|
|
11
13
|
import type { AppContract } from "@interchained/portal-contract";
|
|
14
|
+
import { type Patch, type PatchStore } from "./patch.js";
|
|
12
15
|
export interface SentinelReview {
|
|
13
16
|
approved: boolean;
|
|
14
17
|
summary: string;
|
|
@@ -20,10 +23,14 @@ export interface SentinelReview {
|
|
|
20
23
|
note: string;
|
|
21
24
|
}>;
|
|
22
25
|
}
|
|
26
|
+
export interface SentinelResult {
|
|
27
|
+
review: SentinelReview;
|
|
28
|
+
/** True when the Sentinel both approved AND applied the patch to disk */
|
|
29
|
+
applied: boolean;
|
|
30
|
+
}
|
|
23
31
|
export declare class Sentinel {
|
|
24
32
|
private client;
|
|
25
33
|
constructor(config?: Partial<AiAssistConfig>);
|
|
26
|
-
/** Review a generated patch against the app contract */
|
|
27
34
|
review(opts: {
|
|
28
35
|
contract: AppContract;
|
|
29
36
|
filePath: string;
|
|
@@ -31,5 +38,23 @@ export declare class Sentinel {
|
|
|
31
38
|
proposed: string;
|
|
32
39
|
agentTask: string;
|
|
33
40
|
}): Promise<SentinelReview>;
|
|
41
|
+
/**
|
|
42
|
+
* Review the patch and, if approved, apply it to disk.
|
|
43
|
+
*
|
|
44
|
+
* This is the primary entry point. The Sentinel is both judge and executor:
|
|
45
|
+
* - Approved → patch is written to disk, status set to "applied"
|
|
46
|
+
* - Rejected → nothing is written, violations are surfaced to the caller
|
|
47
|
+
*
|
|
48
|
+
* Pass `overrideApply: true` to apply even if the Sentinel rejects
|
|
49
|
+
* (e.g. explicit human "apply anyway" action in the CLI).
|
|
50
|
+
*/
|
|
51
|
+
reviewAndApply(opts: {
|
|
52
|
+
patch: Patch;
|
|
53
|
+
projectRoot: string;
|
|
54
|
+
store: PatchStore;
|
|
55
|
+
contract: AppContract;
|
|
56
|
+
agentTask: string;
|
|
57
|
+
overrideApply?: boolean;
|
|
58
|
+
}): Promise<SentinelResult>;
|
|
34
59
|
}
|
|
35
60
|
//# sourceMappingURL=sentinel.d.ts.map
|
package/dist/sentinel.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sentinel.d.ts","sourceRoot":"","sources":["../src/sentinel.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"sentinel.d.ts","sourceRoot":"","sources":["../src/sentinel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAc,KAAK,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAErE,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,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,cAAc,CAAC;IACvB,yEAAyE;IACzE,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAiB;gBAEnB,MAAM,GAAE,OAAO,CAAC,cAAc,CAAM;IAe1C,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;IAgE3B;;;;;;;;;OASG;IACG,cAAc,CAAC,IAAI,EAAE;QACzB,KAAK,EAAE,KAAK,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,UAAU,CAAC;QAClB,QAAQ,EAAE,WAAW,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,OAAO,CAAC;KACzB,GAAG,OAAO,CAAC,cAAc,CAAC;CA8B5B"}
|
package/dist/sentinel.js
CHANGED
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Sentinel — reviews Runner output
|
|
2
|
+
* Sentinel — reviews Runner output and, if approved, applies the patch itself.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* The Sentinel is both the gate and the final patch auditor:
|
|
5
|
+
* - APPROVED → Sentinel writes the file, marks the patch "applied"
|
|
6
|
+
* - REJECTED → Sentinel blocks, surfaces violations, human must override
|
|
7
|
+
*
|
|
8
|
+
* Uses a stronger/slower model than the Runner so it can catch what the
|
|
9
|
+
* runner missed: brand violations, hallucinated data, forbidden claims,
|
|
10
|
+
* regressions, and contract drift.
|
|
9
11
|
*/
|
|
10
12
|
import { AiAssistClient } from "./aiassist.js";
|
|
13
|
+
import { applyPatch } from "./patch.js";
|
|
11
14
|
export class Sentinel {
|
|
12
15
|
client;
|
|
13
16
|
constructor(config = {}) {
|
|
14
17
|
this.client = new AiAssistClient({
|
|
15
|
-
apiKey: config.apiKey ??
|
|
18
|
+
apiKey: config.apiKey ??
|
|
19
|
+
process.env["AIASSIST_API_KEY"] ??
|
|
20
|
+
process.env["VITE_AIAS_API_KEY"] ??
|
|
21
|
+
"",
|
|
16
22
|
baseUrl: config.baseUrl,
|
|
17
23
|
model: config.model ?? "gpt-4o",
|
|
18
24
|
timeoutMs: config.timeoutMs ?? 90_000,
|
|
19
25
|
});
|
|
20
26
|
}
|
|
21
|
-
|
|
27
|
+
// ── Core review ─────────────────────────────────────────────────────────────
|
|
22
28
|
async review(opts) {
|
|
23
29
|
const contractStr = JSON.stringify(opts.contract, null, 2);
|
|
24
30
|
const prompt = `You are a code sentinel reviewing an AI-generated patch.
|
|
@@ -49,17 +55,24 @@ Check for:
|
|
|
49
55
|
4. Hallucinated data (phone numbers, prices, names not in data sources)
|
|
50
56
|
5. Whether the patch faithfully serves the stated goals
|
|
51
57
|
6. Whether it actually fixes the agent task
|
|
58
|
+
7. Any obvious regressions or broken imports
|
|
52
59
|
|
|
53
60
|
Respond as JSON:
|
|
54
61
|
{
|
|
55
62
|
"approved": true/false,
|
|
56
63
|
"summary": "one sentence summary of what the patch does",
|
|
57
|
-
"violations": ["list of issues if any"],
|
|
64
|
+
"violations": ["list of issues if any — empty array if clean"],
|
|
58
65
|
"annotations": [{"line": null, "type": "ok|warning|violation", "note": "..."}]
|
|
59
66
|
}`;
|
|
60
|
-
const raw = await this.client.complete(prompt, undefined, {
|
|
67
|
+
const raw = await this.client.complete(prompt, undefined, {
|
|
68
|
+
temperature: 0.1,
|
|
69
|
+
maxTokens: 2_000,
|
|
70
|
+
});
|
|
61
71
|
try {
|
|
62
|
-
const jsonStr = raw
|
|
72
|
+
const jsonStr = raw
|
|
73
|
+
.replace(/^```json\s*/i, "")
|
|
74
|
+
.replace(/\s*```$/, "")
|
|
75
|
+
.trim();
|
|
63
76
|
return JSON.parse(jsonStr);
|
|
64
77
|
}
|
|
65
78
|
catch {
|
|
@@ -71,5 +84,38 @@ Respond as JSON:
|
|
|
71
84
|
};
|
|
72
85
|
}
|
|
73
86
|
}
|
|
87
|
+
// ── Gate + apply ─────────────────────────────────────────────────────────────
|
|
88
|
+
/**
|
|
89
|
+
* Review the patch and, if approved, apply it to disk.
|
|
90
|
+
*
|
|
91
|
+
* This is the primary entry point. The Sentinel is both judge and executor:
|
|
92
|
+
* - Approved → patch is written to disk, status set to "applied"
|
|
93
|
+
* - Rejected → nothing is written, violations are surfaced to the caller
|
|
94
|
+
*
|
|
95
|
+
* Pass `overrideApply: true` to apply even if the Sentinel rejects
|
|
96
|
+
* (e.g. explicit human "apply anyway" action in the CLI).
|
|
97
|
+
*/
|
|
98
|
+
async reviewAndApply(opts) {
|
|
99
|
+
const review = await this.review({
|
|
100
|
+
contract: opts.contract,
|
|
101
|
+
filePath: opts.patch.file,
|
|
102
|
+
original: opts.patch.original,
|
|
103
|
+
proposed: opts.patch.proposed,
|
|
104
|
+
agentTask: opts.agentTask,
|
|
105
|
+
});
|
|
106
|
+
// Persist sentinel verdict onto the patch record
|
|
107
|
+
await opts.store.update(opts.patch.id, {
|
|
108
|
+
sentinelApproved: review.approved,
|
|
109
|
+
sentinelSummary: review.summary,
|
|
110
|
+
sentinelViolations: review.violations,
|
|
111
|
+
});
|
|
112
|
+
const shouldApply = review.approved || opts.overrideApply;
|
|
113
|
+
if (shouldApply) {
|
|
114
|
+
await opts.store.update(opts.patch.id, { status: "approved" });
|
|
115
|
+
await applyPatch({ ...opts.patch, status: "approved" }, opts.projectRoot, opts.store);
|
|
116
|
+
return { review, applied: true };
|
|
117
|
+
}
|
|
118
|
+
return { review, applied: false };
|
|
119
|
+
}
|
|
74
120
|
}
|
|
75
121
|
//# sourceMappingURL=sentinel.js.map
|
package/dist/sentinel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sentinel.js","sourceRoot":"","sources":["../src/sentinel.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"sentinel.js","sourceRoot":"","sources":["../src/sentinel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAuB,MAAM,eAAe,CAAC;AAEpE,OAAO,EAAE,UAAU,EAA+B,MAAM,YAAY,CAAC;AAoBrE,MAAM,OAAO,QAAQ;IACX,MAAM,CAAiB;IAE/B,YAAY,SAAkC,EAAE;QAC9C,IAAI,CAAC,MAAM,GAAG,IAAI,cAAc,CAAC;YAC/B,MAAM,EACJ,MAAM,CAAC,MAAM;gBACb,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;gBAC/B,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;gBAChC,EAAE;YACJ,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,QAAQ;YAC/B,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,MAAM;SACtC,CAAC,CAAC;IACL,CAAC;IAED,+EAA+E;IAE/E,KAAK,CAAC,MAAM,CAAC,IAMZ;QACC,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAE3D,MAAM,MAAM,GAAG;;;;;EAKjB,WAAW;;;cAGC,IAAI,CAAC,SAAS;QACpB,IAAI,CAAC,QAAQ;;;;EAInB,IAAI,CAAC,QAAQ;;;;;EAKb,IAAI,CAAC,QAAQ;;;;;;;;;;;;;;;;;;EAkBb,CAAC;QAEC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE;YACxD,WAAW,EAAE,GAAG;YAChB,SAAS,EAAE,KAAK;SACjB,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,GAAG;iBAChB,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;iBAC3B,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;iBACtB,IAAI,EAAE,CAAC;YACV,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAmB,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;gBACL,QAAQ,EAAE,KAAK;gBACf,OAAO,EAAE,mEAAmE;gBAC5E,UAAU,EAAE,CAAC,+BAA+B,CAAC;gBAC7C,WAAW,EAAE,EAAE;aAChB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,gFAAgF;IAEhF;;;;;;;;;OASG;IACH,KAAK,CAAC,cAAc,CAAC,IAOpB;QACC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC;YAC/B,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI;YACzB,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ;YAC7B,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ;YAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC,CAAC;QAEH,iDAAiD;QACjD,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE;YACrC,gBAAgB,EAAE,MAAM,CAAC,QAAQ;YACjC,eAAe,EAAE,MAAM,CAAC,OAAO;YAC/B,kBAAkB,EAAE,MAAM,CAAC,UAAU;SACtC,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,aAAa,CAAC;QAE1D,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;YAC/D,MAAM,UAAU,CACd,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,EACrC,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,KAAK,CACX,CAAC;YACF,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACnC,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACpC,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interchained/portal-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Agent runtime for Portal — AiAssist.net runner/sentinel, audit, generate, improve, guard",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"author": "Interchained <dev@interchained.org>",
|
|
@@ -18,10 +18,17 @@
|
|
|
18
18
|
"README.md",
|
|
19
19
|
"LICENSE"
|
|
20
20
|
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc",
|
|
23
|
+
"dev": "tsc --watch",
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"clean": "rm -rf dist",
|
|
26
|
+
"prepublishOnly": "npm run typecheck && npm run build"
|
|
27
|
+
},
|
|
21
28
|
"dependencies": {
|
|
29
|
+
"@interchained/portal-contract": "workspace:*",
|
|
22
30
|
"picocolors": "^1.0.1",
|
|
23
|
-
"ora": "^8.0.1"
|
|
24
|
-
"@interchained/portal-contract": "0.1.0"
|
|
31
|
+
"ora": "^8.0.1"
|
|
25
32
|
},
|
|
26
33
|
"devDependencies": {
|
|
27
34
|
"@types/node": "^20.12.7",
|
|
@@ -32,11 +39,5 @@
|
|
|
32
39
|
},
|
|
33
40
|
"publishConfig": {
|
|
34
41
|
"access": "public"
|
|
35
|
-
},
|
|
36
|
-
"scripts": {
|
|
37
|
-
"build": "tsc",
|
|
38
|
-
"dev": "tsc --watch",
|
|
39
|
-
"typecheck": "tsc --noEmit",
|
|
40
|
-
"clean": "rm -rf dist"
|
|
41
42
|
}
|
|
42
|
-
}
|
|
43
|
+
}
|