@interchained/portal-agent 0.1.2 → 0.1.3
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 +11 -4
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +3 -0
- package/dist/generate.js.map +1 -1
- package/dist/sentinel.d.ts +36 -18
- package/dist/sentinel.d.ts.map +1 -1
- package/dist/sentinel.js +105 -45
- package/dist/sentinel.js.map +1 -1
- package/package.json +1 -1
package/dist/generate.d.ts
CHANGED
|
@@ -22,14 +22,21 @@ export interface GenerateOptions {
|
|
|
22
22
|
}
|
|
23
23
|
export interface GenerateResult {
|
|
24
24
|
patch: Patch;
|
|
25
|
-
/** Set whenever a Sentinel ran
|
|
25
|
+
/** Set whenever a Sentinel ran */
|
|
26
26
|
sentinelReview?: SentinelReview;
|
|
27
27
|
/**
|
|
28
|
-
* True when the Sentinel
|
|
29
|
-
* False when sentinel
|
|
30
|
-
* The CLI uses this to skip / show the human approval prompt.
|
|
28
|
+
* True when the Sentinel applied the patch (clean or self-corrected).
|
|
29
|
+
* False when no sentinel ran — human approval prompt shown instead.
|
|
31
30
|
*/
|
|
32
31
|
sentinelApplied: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Populated when the Sentinel had to self-correct the patch before applying.
|
|
34
|
+
* Undefined when the patch was clean.
|
|
35
|
+
*/
|
|
36
|
+
sentinelFix?: {
|
|
37
|
+
summary: string;
|
|
38
|
+
changes: string[];
|
|
39
|
+
};
|
|
33
40
|
}
|
|
34
41
|
/**
|
|
35
42
|
* Generate a new page component from a PageContract.
|
package/dist/generate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,
|
|
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,kCAAkC;IAClC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC;;;OAGG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CACtD;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,CAyDzB;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
|
@@ -55,6 +55,9 @@ export async function generatePage(page, opts, store) {
|
|
|
55
55
|
patch,
|
|
56
56
|
sentinelReview: result.review,
|
|
57
57
|
sentinelApplied: result.applied,
|
|
58
|
+
sentinelFix: result.fix
|
|
59
|
+
? { summary: result.fix.summary, changes: result.fix.changes }
|
|
60
|
+
: undefined,
|
|
58
61
|
};
|
|
59
62
|
}
|
|
60
63
|
// ── No sentinel — patch stays "pending", human decides ────────────────────
|
package/dist/generate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAc,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"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"}
|
package/dist/sentinel.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Sentinel — reviews
|
|
2
|
+
* Sentinel — reviews, auto-corrects, and applies patches.
|
|
3
3
|
*
|
|
4
|
-
* The Sentinel
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* The Sentinel never just rejects. Its contract:
|
|
5
|
+
* 1. Review the Runner's output against the app contract
|
|
6
|
+
* 2. If clean → apply as-is
|
|
7
|
+
* 3. If issues → self-correct (call AI to fix the violations)
|
|
8
|
+
* 4. Apply the corrected version
|
|
9
|
+
* 5. Report what was fixed
|
|
7
10
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* regressions, and contract drift.
|
|
11
|
+
* The human always sees a result, never a gate.
|
|
12
|
+
* The only terminal failure is an unrecoverable AI error.
|
|
11
13
|
*/
|
|
12
14
|
import { type AiAssistConfig } from "./aiassist.js";
|
|
13
15
|
import type { AppContract } from "@interchained/portal-contract";
|
|
@@ -16,17 +18,29 @@ export interface SentinelReview {
|
|
|
16
18
|
approved: boolean;
|
|
17
19
|
summary: string;
|
|
18
20
|
violations: string[];
|
|
19
|
-
/** Diff-friendly line annotations */
|
|
20
21
|
annotations: Array<{
|
|
21
22
|
line?: number;
|
|
22
23
|
type: "ok" | "warning" | "violation";
|
|
23
24
|
note: string;
|
|
24
25
|
}>;
|
|
25
26
|
}
|
|
27
|
+
export interface SentinelFix {
|
|
28
|
+
/** Plain-English summary of what the sentinel changed */
|
|
29
|
+
summary: string;
|
|
30
|
+
/** Each item is one fix the sentinel applied */
|
|
31
|
+
changes: string[];
|
|
32
|
+
/** The corrected file content */
|
|
33
|
+
correctedContent: string;
|
|
34
|
+
}
|
|
26
35
|
export interface SentinelResult {
|
|
27
36
|
review: SentinelReview;
|
|
28
|
-
/**
|
|
29
|
-
applied:
|
|
37
|
+
/** Always true — sentinel either applied clean or applied its own fix */
|
|
38
|
+
applied: true;
|
|
39
|
+
/**
|
|
40
|
+
* Populated when the sentinel had to self-correct.
|
|
41
|
+
* Undefined when the patch was clean and applied as-is.
|
|
42
|
+
*/
|
|
43
|
+
fix?: SentinelFix;
|
|
30
44
|
}
|
|
31
45
|
export declare class Sentinel {
|
|
32
46
|
private client;
|
|
@@ -38,15 +52,20 @@ export declare class Sentinel {
|
|
|
38
52
|
proposed: string;
|
|
39
53
|
agentTask: string;
|
|
40
54
|
}): Promise<SentinelReview>;
|
|
55
|
+
fix(opts: {
|
|
56
|
+
contract: AppContract;
|
|
57
|
+
filePath: string;
|
|
58
|
+
original: string;
|
|
59
|
+
proposed: string;
|
|
60
|
+
violations: string[];
|
|
61
|
+
agentTask: string;
|
|
62
|
+
}): Promise<SentinelFix>;
|
|
41
63
|
/**
|
|
42
|
-
*
|
|
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
|
|
64
|
+
* The primary entry point.
|
|
47
65
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
66
|
+
* Always applies something to disk. Never rejects to the caller.
|
|
67
|
+
* - Clean patch → apply as-is, fix undefined
|
|
68
|
+
* - Dirty patch → self-correct → apply corrected version, fix populated
|
|
50
69
|
*/
|
|
51
70
|
reviewAndApply(opts: {
|
|
52
71
|
patch: Patch;
|
|
@@ -54,7 +73,6 @@ export declare class Sentinel {
|
|
|
54
73
|
store: PatchStore;
|
|
55
74
|
contract: AppContract;
|
|
56
75
|
agentTask: string;
|
|
57
|
-
overrideApply?: boolean;
|
|
58
76
|
}): Promise<SentinelResult>;
|
|
59
77
|
}
|
|
60
78
|
//# 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;;;;;;;;;;;;GAYG;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;AAIrE,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,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,WAAW;IAC1B,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iCAAiC;IACjC,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,cAAc,CAAC;IACvB,yEAAyE;IACzE,OAAO,EAAE,IAAI,CAAC;IACd;;;OAGG;IACH,GAAG,CAAC,EAAE,WAAW,CAAC;CACnB;AAID,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;IA0DrB,GAAG,CAAC,IAAI,EAAE;QACd,QAAQ,EAAE,WAAW,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,WAAW,CAAC;IAqDxB;;;;;;OAMG;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;KACnB,GAAG,OAAO,CAAC,cAAc,CAAC;CA2C5B"}
|
package/dist/sentinel.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Sentinel — reviews
|
|
2
|
+
* Sentinel — reviews, auto-corrects, and applies patches.
|
|
3
3
|
*
|
|
4
|
-
* The Sentinel
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* The Sentinel never just rejects. Its contract:
|
|
5
|
+
* 1. Review the Runner's output against the app contract
|
|
6
|
+
* 2. If clean → apply as-is
|
|
7
|
+
* 3. If issues → self-correct (call AI to fix the violations)
|
|
8
|
+
* 4. Apply the corrected version
|
|
9
|
+
* 5. Report what was fixed
|
|
7
10
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* regressions, and contract drift.
|
|
11
|
+
* The human always sees a result, never a gate.
|
|
12
|
+
* The only terminal failure is an unrecoverable AI error.
|
|
11
13
|
*/
|
|
12
14
|
import { AiAssistClient } from "./aiassist.js";
|
|
13
15
|
import { applyPatch } from "./patch.js";
|
|
16
|
+
// ── Sentinel ──────────────────────────────────────────────────────────────────
|
|
14
17
|
export class Sentinel {
|
|
15
18
|
client;
|
|
16
19
|
constructor(config = {}) {
|
|
@@ -21,18 +24,17 @@ export class Sentinel {
|
|
|
21
24
|
"",
|
|
22
25
|
baseUrl: config.baseUrl,
|
|
23
26
|
model: config.model ?? "gpt-4o",
|
|
24
|
-
timeoutMs: config.timeoutMs ??
|
|
27
|
+
timeoutMs: config.timeoutMs ?? 120_000,
|
|
25
28
|
});
|
|
26
29
|
}
|
|
27
|
-
// ──
|
|
30
|
+
// ── 1. Review ───────────────────────────────────────────────────────────────
|
|
28
31
|
async review(opts) {
|
|
29
|
-
const contractStr = JSON.stringify(opts.contract, null, 2);
|
|
30
32
|
const prompt = `You are a code sentinel reviewing an AI-generated patch.
|
|
31
|
-
|
|
33
|
+
Verify the patch is safe, faithful to the app contract, and actually solves the task.
|
|
32
34
|
|
|
33
35
|
App Contract:
|
|
34
36
|
\`\`\`json
|
|
35
|
-
${
|
|
37
|
+
${JSON.stringify(opts.contract, null, 2)}
|
|
36
38
|
\`\`\`
|
|
37
39
|
|
|
38
40
|
Agent task: ${opts.agentTask}
|
|
@@ -49,53 +51,97 @@ ${opts.proposed}
|
|
|
49
51
|
\`\`\`
|
|
50
52
|
|
|
51
53
|
Check for:
|
|
52
|
-
1. Forbidden claims
|
|
53
|
-
2. Forbidden phrases
|
|
54
|
-
3.
|
|
55
|
-
4. Hallucinated data (
|
|
56
|
-
5. Whether the patch
|
|
57
|
-
6.
|
|
58
|
-
7. Any obvious regressions or broken imports
|
|
54
|
+
1. Forbidden claims (contract.policies.forbiddenClaims)
|
|
55
|
+
2. Forbidden phrases (contract.brand.forbiddenPhrases)
|
|
56
|
+
3. Unauthorised brand color changes
|
|
57
|
+
4. Hallucinated data (numbers, prices, names not in data sources)
|
|
58
|
+
5. Whether the patch actually solves the task
|
|
59
|
+
6. Obvious regressions or broken imports
|
|
59
60
|
|
|
60
|
-
Respond as JSON:
|
|
61
|
+
Respond as JSON only — no markdown fences:
|
|
61
62
|
{
|
|
62
|
-
"approved": true
|
|
63
|
-
"summary": "one sentence
|
|
64
|
-
"violations": [
|
|
65
|
-
"annotations": [
|
|
63
|
+
"approved": true,
|
|
64
|
+
"summary": "one sentence",
|
|
65
|
+
"violations": [],
|
|
66
|
+
"annotations": []
|
|
66
67
|
}`;
|
|
67
68
|
const raw = await this.client.complete(prompt, undefined, {
|
|
68
69
|
temperature: 0.1,
|
|
69
70
|
maxTokens: 2_000,
|
|
70
71
|
});
|
|
71
72
|
try {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
.replace(/\s*```$/, "")
|
|
75
|
-
.trim();
|
|
76
|
-
return JSON.parse(jsonStr);
|
|
73
|
+
const cleaned = raw.replace(/^```json\s*/i, "").replace(/\s*```$/, "").trim();
|
|
74
|
+
return JSON.parse(cleaned);
|
|
77
75
|
}
|
|
78
76
|
catch {
|
|
79
77
|
return {
|
|
80
78
|
approved: false,
|
|
81
|
-
summary: "Sentinel could not parse its
|
|
79
|
+
summary: "Sentinel could not parse its review — will attempt self-correction.",
|
|
82
80
|
violations: ["Unparseable sentinel response"],
|
|
83
81
|
annotations: [],
|
|
84
82
|
};
|
|
85
83
|
}
|
|
86
84
|
}
|
|
87
|
-
// ──
|
|
85
|
+
// ── 2. Self-correct ─────────────────────────────────────────────────────────
|
|
86
|
+
async fix(opts) {
|
|
87
|
+
const prompt = `You are a code sentinel. An AI agent generated a patch that has the following issues:
|
|
88
|
+
|
|
89
|
+
${opts.violations.map((v, i) => `${i + 1}. ${v}`).join("\n")}
|
|
90
|
+
|
|
91
|
+
Your job: rewrite the proposed file to fix ALL of these issues while still fulfilling the original task.
|
|
92
|
+
Keep every part of the patch that was correct. Only change what violates the rules.
|
|
93
|
+
|
|
94
|
+
Original task: ${opts.agentTask}
|
|
95
|
+
File: ${opts.filePath}
|
|
96
|
+
|
|
97
|
+
App Contract (source of truth for brand, policies, data):
|
|
98
|
+
\`\`\`json
|
|
99
|
+
${JSON.stringify(opts.contract, null, 2)}
|
|
100
|
+
\`\`\`
|
|
101
|
+
|
|
102
|
+
ORIGINAL FILE:
|
|
103
|
+
\`\`\`tsx
|
|
104
|
+
${opts.original}
|
|
105
|
+
\`\`\`
|
|
106
|
+
|
|
107
|
+
PROPOSED (with violations):
|
|
108
|
+
\`\`\`tsx
|
|
109
|
+
${opts.proposed}
|
|
110
|
+
\`\`\`
|
|
111
|
+
|
|
112
|
+
Respond as JSON only — no markdown fences:
|
|
113
|
+
{
|
|
114
|
+
"correctedContent": "<full corrected file content as a string>",
|
|
115
|
+
"changes": ["Fixed: ...", "Removed: ...", "Replaced: ..."],
|
|
116
|
+
"summary": "one sentence describing what was corrected"
|
|
117
|
+
}`;
|
|
118
|
+
const raw = await this.client.complete(prompt, undefined, {
|
|
119
|
+
temperature: 0.2,
|
|
120
|
+
maxTokens: 8_000,
|
|
121
|
+
});
|
|
122
|
+
try {
|
|
123
|
+
const cleaned = raw.replace(/^```json\s*/i, "").replace(/\s*```$/, "").trim();
|
|
124
|
+
return JSON.parse(cleaned);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Last resort: return the proposed content unchanged with a note
|
|
128
|
+
return {
|
|
129
|
+
correctedContent: opts.proposed,
|
|
130
|
+
changes: ["Sentinel could not parse its own correction — applied original patch."],
|
|
131
|
+
summary: "Correction parse failed; original patch applied.",
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// ── 3. Review → fix if needed → apply ──────────────────────────────────────
|
|
88
136
|
/**
|
|
89
|
-
*
|
|
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
|
|
137
|
+
* The primary entry point.
|
|
94
138
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
139
|
+
* Always applies something to disk. Never rejects to the caller.
|
|
140
|
+
* - Clean patch → apply as-is, fix undefined
|
|
141
|
+
* - Dirty patch → self-correct → apply corrected version, fix populated
|
|
97
142
|
*/
|
|
98
143
|
async reviewAndApply(opts) {
|
|
144
|
+
// Step 1 — review
|
|
99
145
|
const review = await this.review({
|
|
100
146
|
contract: opts.contract,
|
|
101
147
|
filePath: opts.patch.file,
|
|
@@ -103,19 +149,33 @@ Respond as JSON:
|
|
|
103
149
|
proposed: opts.patch.proposed,
|
|
104
150
|
agentTask: opts.agentTask,
|
|
105
151
|
});
|
|
106
|
-
// Persist sentinel verdict onto the patch record
|
|
107
152
|
await opts.store.update(opts.patch.id, {
|
|
108
153
|
sentinelApproved: review.approved,
|
|
109
154
|
sentinelSummary: review.summary,
|
|
110
155
|
sentinelViolations: review.violations,
|
|
111
156
|
});
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
157
|
+
// Step 2 — self-correct if needed
|
|
158
|
+
let finalContent = opts.patch.proposed;
|
|
159
|
+
let fix;
|
|
160
|
+
if (!review.approved && review.violations.length > 0) {
|
|
161
|
+
fix = await this.fix({
|
|
162
|
+
contract: opts.contract,
|
|
163
|
+
filePath: opts.patch.file,
|
|
164
|
+
original: opts.patch.original,
|
|
165
|
+
proposed: opts.patch.proposed,
|
|
166
|
+
violations: review.violations,
|
|
167
|
+
agentTask: opts.agentTask,
|
|
168
|
+
});
|
|
169
|
+
finalContent = fix.correctedContent;
|
|
170
|
+
await opts.store.update(opts.patch.id, {
|
|
171
|
+
sentinelSummary: fix.summary,
|
|
172
|
+
});
|
|
117
173
|
}
|
|
118
|
-
|
|
174
|
+
// Step 3 — apply (original or corrected)
|
|
175
|
+
const patchToApply = { ...opts.patch, proposed: finalContent, status: "approved" };
|
|
176
|
+
await opts.store.update(opts.patch.id, { status: "approved", proposed: finalContent });
|
|
177
|
+
await applyPatch(patchToApply, opts.projectRoot, opts.store);
|
|
178
|
+
return { review, applied: true, fix };
|
|
119
179
|
}
|
|
120
180
|
}
|
|
121
181
|
//# 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":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interchained/portal-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
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>",
|