@gakim-digital/dexter-bridge 0.11.4 → 0.11.6
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/README.md +20 -11
- package/package.json +3 -2
- package/src/agent.js +47 -16
- package/src/cli.js +37 -6
- package/src/config.js +6 -2
- package/src/framerAgentTools.js +353 -72
- package/src/framerBehaviorContract.js +516 -103
- package/src/protocol.js +34 -12
- package/src/providers/claudeAgentSdk.js +169 -0
- package/src/providers/codexAppServer.js +68 -12
- package/src/runtimeProfiles.js +27 -2
package/src/framerAgentTools.js
CHANGED
|
@@ -24,6 +24,8 @@ const DEFAULT_COMMAND_TIMEOUT_MS = 5 * 60_000;
|
|
|
24
24
|
const SESSION_TIMEOUT_MS = 10 * 60_000;
|
|
25
25
|
const MAX_UNSAFE_WRITE_FAILURES = 2;
|
|
26
26
|
const MAX_LOW_LEVEL_WRITE_FAILURES = 3;
|
|
27
|
+
const MAX_INTERACTION_VERIFICATION_ATTEMPTS_PER_BEHAVIOR = 3;
|
|
28
|
+
const MAX_INTERACTION_REPAIRS_PER_BEHAVIOR = 1;
|
|
27
29
|
const MAX_GUIDANCE_BYTES = 180 * 1024;
|
|
28
30
|
const MAX_GUIDANCE_FILE_BYTES = 48 * 1024;
|
|
29
31
|
const PROJECT_ID_PATTERN = /^[A-Za-z0-9]{20}$/;
|
|
@@ -189,6 +191,15 @@ export const FRAMER_AGENT_TOOL_DEFINITIONS = [
|
|
|
189
191
|
type: 'array',
|
|
190
192
|
maxItems: 20,
|
|
191
193
|
items: { type: 'string', minLength: 1, maxLength: 500 },
|
|
194
|
+
description:
|
|
195
|
+
'Optional exact literal text that must exist in the interaction target. Do not put behavioral descriptions or prose assertions here.',
|
|
196
|
+
},
|
|
197
|
+
expectedDestinations: {
|
|
198
|
+
type: 'array',
|
|
199
|
+
maxItems: 20,
|
|
200
|
+
items: { type: 'string', minLength: 1, maxLength: 500 },
|
|
201
|
+
description:
|
|
202
|
+
'Required for navigation behavior. Use exact routes or URLs such as /contact.',
|
|
192
203
|
},
|
|
193
204
|
initialState: {
|
|
194
205
|
type: 'string',
|
|
@@ -237,6 +248,25 @@ export const FRAMER_AGENT_TOOL_DEFINITIONS = [
|
|
|
237
248
|
maxLength: 800,
|
|
238
249
|
},
|
|
239
250
|
representativeFirst: { type: 'boolean', default: true },
|
|
251
|
+
targetNodeIds: {
|
|
252
|
+
type: 'array',
|
|
253
|
+
maxItems: 50,
|
|
254
|
+
uniqueItems: true,
|
|
255
|
+
items: {
|
|
256
|
+
type: 'string',
|
|
257
|
+
minLength: 1,
|
|
258
|
+
maxLength: 200,
|
|
259
|
+
},
|
|
260
|
+
description:
|
|
261
|
+
'All intended target node IDs. Required when expectedTargets is greater than one.',
|
|
262
|
+
},
|
|
263
|
+
representativeTargetNodeId: {
|
|
264
|
+
type: 'string',
|
|
265
|
+
minLength: 1,
|
|
266
|
+
maxLength: 200,
|
|
267
|
+
description:
|
|
268
|
+
'The single target to implement and verify before fan-out.',
|
|
269
|
+
},
|
|
240
270
|
},
|
|
241
271
|
required: ['behaviorId', 'mechanism', 'rationale'],
|
|
242
272
|
additionalProperties: false,
|
|
@@ -278,7 +308,7 @@ export const FRAMER_AGENT_TOOL_DEFINITIONS = [
|
|
|
278
308
|
{
|
|
279
309
|
name: 'framer_verify_interactions',
|
|
280
310
|
description:
|
|
281
|
-
'Verify the observable behavior contract from
|
|
311
|
+
'Verify the observable behavior contract from live Framer evidence. Results are verified, contradicted, or unknown. Unknown evidence never proves failure and must not trigger mutation. Native links and component-control navigation are verified from destinations, not onTap. You may add a grounded semanticAssessment; it cannot override a deterministic contradiction or authorize writes.',
|
|
282
312
|
inputSchema: {
|
|
283
313
|
type: 'object',
|
|
284
314
|
properties: {
|
|
@@ -310,6 +340,11 @@ export const FRAMER_AGENT_TOOL_DEFINITIONS = [
|
|
|
310
340
|
minLength: 1,
|
|
311
341
|
maxLength: 80,
|
|
312
342
|
},
|
|
343
|
+
expectedDestination: {
|
|
344
|
+
type: 'string',
|
|
345
|
+
minLength: 1,
|
|
346
|
+
maxLength: 500,
|
|
347
|
+
},
|
|
313
348
|
targetNodeId: {
|
|
314
349
|
type: 'string',
|
|
315
350
|
minLength: 1,
|
|
@@ -350,12 +385,34 @@ export const FRAMER_AGENT_TOOL_DEFINITIONS = [
|
|
|
350
385
|
minLength: 1,
|
|
351
386
|
maxLength: 200,
|
|
352
387
|
},
|
|
388
|
+
semanticAssessment: {
|
|
389
|
+
type: 'object',
|
|
390
|
+
properties: {
|
|
391
|
+
verdict: {
|
|
392
|
+
type: 'string',
|
|
393
|
+
enum: ['verified', 'contradicted', 'unknown'],
|
|
394
|
+
},
|
|
395
|
+
rationale: {
|
|
396
|
+
type: 'string',
|
|
397
|
+
minLength: 10,
|
|
398
|
+
maxLength: 1200,
|
|
399
|
+
},
|
|
400
|
+
evidenceNodeIds: {
|
|
401
|
+
type: 'array',
|
|
402
|
+
maxItems: 50,
|
|
403
|
+
uniqueItems: true,
|
|
404
|
+
items: {
|
|
405
|
+
type: 'string',
|
|
406
|
+
minLength: 1,
|
|
407
|
+
maxLength: 200,
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
required: ['verdict', 'rationale', 'evidenceNodeIds'],
|
|
412
|
+
additionalProperties: false,
|
|
413
|
+
},
|
|
353
414
|
},
|
|
354
|
-
required: [
|
|
355
|
-
'behaviorId',
|
|
356
|
-
'triggerNodeId',
|
|
357
|
-
'event',
|
|
358
|
-
],
|
|
415
|
+
required: ['behaviorId'],
|
|
359
416
|
additionalProperties: false,
|
|
360
417
|
},
|
|
361
418
|
},
|
|
@@ -1053,6 +1110,19 @@ export function createFramerAgentToolRuntime({
|
|
|
1053
1110
|
const activePagePath = normalizePagePath(
|
|
1054
1111
|
assignment?.context?.activePagePath,
|
|
1055
1112
|
);
|
|
1113
|
+
const semanticVerifierMode =
|
|
1114
|
+
['off', 'shadow', 'enforce'].includes(
|
|
1115
|
+
String(
|
|
1116
|
+
assignment?.context?.harnessAuthority?.semanticVerifierMode
|
|
1117
|
+
|| env.DEXTER_FRAMER_SEMANTIC_VERIFIER_MODE
|
|
1118
|
+
|| '',
|
|
1119
|
+
).trim(),
|
|
1120
|
+
)
|
|
1121
|
+
? String(
|
|
1122
|
+
assignment?.context?.harnessAuthority?.semanticVerifierMode
|
|
1123
|
+
|| env.DEXTER_FRAMER_SEMANTIC_VERIFIER_MODE,
|
|
1124
|
+
).trim()
|
|
1125
|
+
: 'enforce';
|
|
1056
1126
|
const authoritativeNodeIds =
|
|
1057
1127
|
authoritativeNodeIdsFromAssignment(assignment);
|
|
1058
1128
|
let sessionId = null;
|
|
@@ -1086,14 +1156,18 @@ export function createFramerAgentToolRuntime({
|
|
|
1086
1156
|
let interactionVerificationCalls = 0;
|
|
1087
1157
|
let interactionVerification = {
|
|
1088
1158
|
completed: false,
|
|
1089
|
-
verificationLevel: '
|
|
1159
|
+
verificationLevel: 'unknown',
|
|
1090
1160
|
checks: [],
|
|
1091
1161
|
behaviors: [],
|
|
1162
|
+
budgetExhaustedBehaviorIds: [],
|
|
1092
1163
|
verifiedAt: null,
|
|
1093
1164
|
};
|
|
1094
1165
|
const representativeVerifiedBehaviorIds = new Set();
|
|
1166
|
+
const representativeMutatedBehaviorIds = new Set();
|
|
1095
1167
|
const representativeRepairNodeIds = new Set();
|
|
1096
|
-
const
|
|
1168
|
+
const representativeRepairBehaviorByNode = new Map();
|
|
1169
|
+
const representativeEvidenceBehaviorByNode = new Map();
|
|
1170
|
+
const behaviorVerificationAttempts = new Map();
|
|
1097
1171
|
const mutationReceipts = [];
|
|
1098
1172
|
let screenshotBeforeMutation = false;
|
|
1099
1173
|
let lastScreenshotSequence = 0;
|
|
@@ -1588,9 +1662,11 @@ export function createFramerAgentToolRuntime({
|
|
|
1588
1662
|
behaviors: taskPlan.behaviors.map((behavior) => ({
|
|
1589
1663
|
...behavior,
|
|
1590
1664
|
expectedContent: [...behavior.expectedContent],
|
|
1665
|
+
expectedDestinations: [...behavior.expectedDestinations],
|
|
1591
1666
|
})),
|
|
1592
1667
|
mechanisms: taskPlan.mechanisms.map((decision) => ({
|
|
1593
1668
|
...decision,
|
|
1669
|
+
targetNodeIds: [...decision.targetNodeIds],
|
|
1594
1670
|
})),
|
|
1595
1671
|
rejectedMechanisms: taskPlan.rejectedMechanisms.map((entry) => ({
|
|
1596
1672
|
...entry,
|
|
@@ -1657,15 +1733,35 @@ export function createFramerAgentToolRuntime({
|
|
|
1657
1733
|
verificationLevel:
|
|
1658
1734
|
verificationCheckCompleted('interactions')
|
|
1659
1735
|
? interactionVerification.verificationLevel
|
|
1660
|
-
: '
|
|
1736
|
+
: 'unknown',
|
|
1661
1737
|
checks: interactionVerification.checks.map((check) => ({
|
|
1662
1738
|
...check,
|
|
1663
|
-
|
|
1739
|
+
contradictions: [...check.contradictions],
|
|
1740
|
+
unknowns: [...check.unknowns],
|
|
1741
|
+
observations: check.observations.map((observation) => ({
|
|
1742
|
+
...observation,
|
|
1743
|
+
})),
|
|
1744
|
+
nextSafeActions: [...check.nextSafeActions],
|
|
1745
|
+
...(check.semanticAssessment
|
|
1746
|
+
? {
|
|
1747
|
+
semanticAssessment: {
|
|
1748
|
+
...check.semanticAssessment,
|
|
1749
|
+
evidenceNodeIds: [
|
|
1750
|
+
...check.semanticAssessment.evidenceNodeIds,
|
|
1751
|
+
],
|
|
1752
|
+
},
|
|
1753
|
+
}
|
|
1754
|
+
: {}),
|
|
1664
1755
|
})),
|
|
1665
1756
|
behaviors: interactionVerification.behaviors.map((behavior) => ({
|
|
1666
1757
|
...behavior,
|
|
1667
|
-
|
|
1758
|
+
contradictions: [...behavior.contradictions],
|
|
1759
|
+
unknowns: [...behavior.unknowns],
|
|
1668
1760
|
})),
|
|
1761
|
+
semanticVerifierMode,
|
|
1762
|
+
budgetExhaustedBehaviorIds: [
|
|
1763
|
+
...interactionVerification.budgetExhaustedBehaviorIds,
|
|
1764
|
+
],
|
|
1669
1765
|
verifiedAt: interactionVerification.verifiedAt,
|
|
1670
1766
|
},
|
|
1671
1767
|
}
|
|
@@ -1720,12 +1816,27 @@ export function createFramerAgentToolRuntime({
|
|
|
1720
1816
|
if (check === 'visual') return 'before-and-after screenshot verification';
|
|
1721
1817
|
return `post-mutation ${check} verification`;
|
|
1722
1818
|
});
|
|
1723
|
-
const
|
|
1724
|
-
|
|
1819
|
+
const interactionDetails = interactionVerification.behaviors
|
|
1820
|
+
.filter((behavior) => behavior.verdict !== 'verified')
|
|
1821
|
+
.map((behavior) => {
|
|
1822
|
+
const detail =
|
|
1823
|
+
behavior.contradictions[0]
|
|
1824
|
+
|| behavior.unknowns[0]
|
|
1825
|
+
|| 'no conclusive live evidence';
|
|
1826
|
+
return `${behavior.behaviorId}=${behavior.verdict} (${detail})`;
|
|
1827
|
+
});
|
|
1828
|
+
const reason = [
|
|
1829
|
+
`The harness ended before completing ${missing.join(' and ')}.`,
|
|
1830
|
+
...(interactionDetails.length > 0
|
|
1831
|
+
? [`Interaction evidence: ${interactionDetails.join('; ')}.`]
|
|
1832
|
+
: []),
|
|
1833
|
+
].join(' ');
|
|
1725
1834
|
return {
|
|
1726
1835
|
...completion,
|
|
1727
1836
|
status: 'failed',
|
|
1728
|
-
summary:
|
|
1837
|
+
summary:
|
|
1838
|
+
String(completion?.summary || '').trim()
|
|
1839
|
+
|| 'The requested changes were applied.',
|
|
1729
1840
|
checks: [
|
|
1730
1841
|
...(Array.isArray(completion?.checks) ? completion.checks : []),
|
|
1731
1842
|
{
|
|
@@ -1756,9 +1867,17 @@ export function createFramerAgentToolRuntime({
|
|
|
1756
1867
|
: 'report that the required baseline screenshot was missed; do not claim visual verification',
|
|
1757
1868
|
);
|
|
1758
1869
|
} else {
|
|
1870
|
+
const interactionState = interactionVerification.behaviors
|
|
1871
|
+
.map((behavior) =>
|
|
1872
|
+
`${behavior.behaviorId}:${behavior.verdict}`)
|
|
1873
|
+
.join(', ');
|
|
1874
|
+
const exhausted =
|
|
1875
|
+
interactionVerification.budgetExhaustedBehaviorIds.length > 0;
|
|
1759
1876
|
missing.push(
|
|
1760
1877
|
check === 'interactions'
|
|
1761
|
-
?
|
|
1878
|
+
? exhausted
|
|
1879
|
+
? `report interaction verification as incomplete without further mutation or repeated verification (${interactionState || 'unknown'})`
|
|
1880
|
+
: `call framer_verify_interactions with canonical evidence node IDs for every declared behavior and chosen mechanism; repair only contradicted evidence, while unknown evidence requires a different read or grounded semantic assessment (${interactionState || 'not yet checked'})`
|
|
1762
1881
|
: `perform a focused read tagged with verifies:["${check}"] after the latest mutation`,
|
|
1763
1882
|
);
|
|
1764
1883
|
}
|
|
@@ -1767,7 +1886,7 @@ export function createFramerAgentToolRuntime({
|
|
|
1767
1886
|
'Continue the same Framer task from its current project state.',
|
|
1768
1887
|
'Do not redo successful work.',
|
|
1769
1888
|
`Before returning, ${missing.join(' and ')}.`,
|
|
1770
|
-
'Repair
|
|
1889
|
+
'Never mutate merely because evidence is unknown. Repair one observed contradiction at most once, then return the required JSON outcome.',
|
|
1771
1890
|
].join(' ');
|
|
1772
1891
|
};
|
|
1773
1892
|
|
|
@@ -1861,22 +1980,70 @@ export function createFramerAgentToolRuntime({
|
|
|
1861
1980
|
`Choose a different mechanism for ${rejectedSelectedMechanisms.map((behavior) => behavior.id).join(', ')} before making another mutation.`,
|
|
1862
1981
|
);
|
|
1863
1982
|
}
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
.
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1983
|
+
const changedNodeIds = new Set(
|
|
1984
|
+
summarizeFramerChanges(changes).commands
|
|
1985
|
+
.flatMap((command) => [
|
|
1986
|
+
command.nodeId,
|
|
1987
|
+
command.sourceNodeId,
|
|
1988
|
+
])
|
|
1989
|
+
.filter(Boolean),
|
|
1990
|
+
);
|
|
1991
|
+
const rawChanges = String(changes || '');
|
|
1992
|
+
const pendingRepresentative = taskPlan.behaviors
|
|
1993
|
+
.filter((behavior) => behavior.expectedTargets > 1)
|
|
1994
|
+
.map((behavior) => ({
|
|
1995
|
+
behavior,
|
|
1996
|
+
decision: taskPlan.mechanisms.find((decision) =>
|
|
1997
|
+
decision.behaviorId === behavior.id),
|
|
1998
|
+
}))
|
|
1999
|
+
.filter(({ decision }) => decision?.representativeFirst)
|
|
2000
|
+
.filter(({ behavior }) =>
|
|
2001
|
+
!representativeVerifiedBehaviorIds.has(behavior.id));
|
|
2002
|
+
for (const { behavior, decision } of pendingRepresentative) {
|
|
2003
|
+
const touchedTargets = decision.targetNodeIds.filter((nodeId) =>
|
|
2004
|
+
rawChanges.includes(nodeId));
|
|
2005
|
+
const touchedEvidence = [...changedNodeIds].filter((nodeId) =>
|
|
2006
|
+
representativeEvidenceBehaviorByNode.get(nodeId) === behavior.id);
|
|
2007
|
+
if (
|
|
2008
|
+
touchedTargets.length > 1
|
|
2009
|
+
|| (
|
|
2010
|
+
touchedTargets.length === 1
|
|
2011
|
+
&& touchedTargets[0] !== decision.representativeTargetNodeId
|
|
2012
|
+
)
|
|
2013
|
+
) {
|
|
2014
|
+
throw toolError(
|
|
2015
|
+
'FRAMER_BEHAVIOR_REPRESENTATIVE_MUTATION_REQUIRED',
|
|
2016
|
+
`Implement only representative target ${decision.representativeTargetNodeId} for ${behavior.id} before changing the remaining targets.`,
|
|
2017
|
+
);
|
|
2018
|
+
}
|
|
2019
|
+
if (touchedTargets.length === 0 && touchedEvidence.length === 0) {
|
|
2020
|
+
continue;
|
|
2021
|
+
}
|
|
2022
|
+
if (!representativeMutatedBehaviorIds.has(behavior.id)) continue;
|
|
2023
|
+
const attempt = behaviorVerificationAttempts.get(behavior.id);
|
|
2024
|
+
const repairForBehavior =
|
|
2025
|
+
isRepresentativeRepair
|
|
2026
|
+
&& repairTargets.every((nodeId) =>
|
|
2027
|
+
representativeRepairBehaviorByNode.get(nodeId) === behavior.id);
|
|
2028
|
+
if (
|
|
2029
|
+
!repairForBehavior
|
|
2030
|
+
|| Number(attempt?.repairs || 0)
|
|
2031
|
+
>= MAX_INTERACTION_REPAIRS_PER_BEHAVIOR
|
|
2032
|
+
) {
|
|
1874
2033
|
throw toolError(
|
|
1875
2034
|
'FRAMER_BEHAVIOR_REPRESENTATIVE_VERIFICATION_REQUIRED',
|
|
1876
|
-
`Verify
|
|
2035
|
+
`Verify representative target ${decision.representativeTargetNodeId} for ${behavior.id} before changing it again or batching the remaining targets.`,
|
|
1877
2036
|
);
|
|
1878
2037
|
}
|
|
2038
|
+
behaviorVerificationAttempts.set(behavior.id, {
|
|
2039
|
+
...attempt,
|
|
2040
|
+
repairs: Number(attempt?.repairs || 0) + 1,
|
|
2041
|
+
});
|
|
1879
2042
|
}
|
|
2043
|
+
return pendingRepresentative
|
|
2044
|
+
.filter(({ decision }) =>
|
|
2045
|
+
rawChanges.includes(decision.representativeTargetNodeId))
|
|
2046
|
+
.map(({ behavior }) => behavior.id);
|
|
1880
2047
|
};
|
|
1881
2048
|
|
|
1882
2049
|
async function invokeUnsafe(name, rawArguments = {}) {
|
|
@@ -2023,7 +2190,12 @@ export function createFramerAgentToolRuntime({
|
|
|
2023
2190
|
const previousMechanisms = new Map(
|
|
2024
2191
|
(taskPlan?.mechanisms || []).map((decision) => [
|
|
2025
2192
|
decision.behaviorId,
|
|
2026
|
-
|
|
2193
|
+
JSON.stringify({
|
|
2194
|
+
mechanism: decision.mechanism,
|
|
2195
|
+
targetNodeIds: decision.targetNodeIds,
|
|
2196
|
+
representativeTargetNodeId:
|
|
2197
|
+
decision.representativeTargetNodeId,
|
|
2198
|
+
}),
|
|
2027
2199
|
]),
|
|
2028
2200
|
);
|
|
2029
2201
|
const behaviorPlan = mergeBehaviorPlans(taskPlan, nextPlan);
|
|
@@ -2041,8 +2213,16 @@ export function createFramerAgentToolRuntime({
|
|
|
2041
2213
|
};
|
|
2042
2214
|
for (const decision of taskPlan.mechanisms) {
|
|
2043
2215
|
const prior = previousMechanisms.get(decision.behaviorId);
|
|
2044
|
-
|
|
2216
|
+
const current = JSON.stringify({
|
|
2217
|
+
mechanism: decision.mechanism,
|
|
2218
|
+
targetNodeIds: decision.targetNodeIds,
|
|
2219
|
+
representativeTargetNodeId:
|
|
2220
|
+
decision.representativeTargetNodeId,
|
|
2221
|
+
});
|
|
2222
|
+
if (prior && prior !== current) {
|
|
2045
2223
|
representativeVerifiedBehaviorIds.delete(decision.behaviorId);
|
|
2224
|
+
representativeMutatedBehaviorIds.delete(decision.behaviorId);
|
|
2225
|
+
behaviorVerificationAttempts.delete(decision.behaviorId);
|
|
2046
2226
|
}
|
|
2047
2227
|
}
|
|
2048
2228
|
result = {
|
|
@@ -2088,21 +2268,43 @@ export function createFramerAgentToolRuntime({
|
|
|
2088
2268
|
rawArguments.checks,
|
|
2089
2269
|
taskPlan,
|
|
2090
2270
|
);
|
|
2271
|
+
const exhaustedBehaviorIds = uniqueStrings(
|
|
2272
|
+
checks
|
|
2273
|
+
.map((check) => check.behaviorId)
|
|
2274
|
+
.filter((behaviorId) =>
|
|
2275
|
+
behaviorVerificationAttempts.get(behaviorId)?.exhausted),
|
|
2276
|
+
);
|
|
2277
|
+
if (exhaustedBehaviorIds.length > 0) {
|
|
2278
|
+
throw toolError(
|
|
2279
|
+
'FRAMER_INTERACTION_VERIFICATION_BUDGET_EXHAUSTED',
|
|
2280
|
+
`Interaction verification is exhausted for ${exhaustedBehaviorIds.join(', ')}. Stop retrying unchanged evidence and report the work as partially verified.`,
|
|
2281
|
+
);
|
|
2282
|
+
}
|
|
2091
2283
|
const pagePath = normalizePagePath(rawArguments.pagePath);
|
|
2092
2284
|
const requestedIds = evidenceNodeIds(checks);
|
|
2285
|
+
const componentIds = uniqueStrings(
|
|
2286
|
+
checks.map((check) => check.componentNodeId),
|
|
2287
|
+
);
|
|
2093
2288
|
const liveResult = await execCode(
|
|
2094
2289
|
[
|
|
2095
2290
|
`const requestedIds = ${JSON.stringify(requestedIds)};`,
|
|
2096
|
-
|
|
2097
|
-
'
|
|
2098
|
-
'
|
|
2099
|
-
|
|
2100
|
-
'
|
|
2101
|
-
`
|
|
2102
|
-
'
|
|
2103
|
-
`
|
|
2104
|
-
'}
|
|
2105
|
-
|
|
2291
|
+
`const componentIds = ${JSON.stringify(componentIds)};`,
|
|
2292
|
+
'const [nodes, componentControls] = await Promise.all([',
|
|
2293
|
+
' Promise.all(requestedIds.map(async (id) => ({',
|
|
2294
|
+
' id,',
|
|
2295
|
+
' node: await framer.agent.serialize({ id, depth: 8 },',
|
|
2296
|
+
` { pagePath: ${JSON.stringify(pagePath)} }).catch(() => null),`,
|
|
2297
|
+
' parentId: await framer.agent.getParentNode({ id },',
|
|
2298
|
+
` { pagePath: ${JSON.stringify(pagePath)} }).then((node) => node?.id ?? null).catch(() => null),`,
|
|
2299
|
+
' ancestorIds: await framer.agent.getAncestors({ id },',
|
|
2300
|
+
` { pagePath: ${JSON.stringify(pagePath)} }).then((nodes) => Array.isArray(nodes) ? nodes.map((node) => node?.id).filter(Boolean) : []).catch(() => []),`,
|
|
2301
|
+
' }))),',
|
|
2302
|
+
' componentIds.length > 0',
|
|
2303
|
+
' ? framer.agent.readComponentControls({ componentIds },',
|
|
2304
|
+
` { pagePath: ${JSON.stringify(pagePath)} }).catch(() => null)`,
|
|
2305
|
+
' : null,',
|
|
2306
|
+
']);',
|
|
2307
|
+
'console.log(JSON.stringify({ nodes, componentControls }, null, 2));',
|
|
2106
2308
|
].join('\n'),
|
|
2107
2309
|
);
|
|
2108
2310
|
const parsed = parseStructuredOutput(liveResult.stdout);
|
|
@@ -2127,52 +2329,115 @@ export function createFramerAgentToolRuntime({
|
|
|
2127
2329
|
context: {
|
|
2128
2330
|
lowLevelWriteCalls,
|
|
2129
2331
|
authorizedInstanceContainerIds,
|
|
2332
|
+
componentControls: parsed?.componentControls || null,
|
|
2333
|
+
semanticVerifierMode,
|
|
2130
2334
|
},
|
|
2131
2335
|
});
|
|
2132
2336
|
markInspection();
|
|
2133
2337
|
interactionVerificationCalls += 1;
|
|
2134
|
-
interactionVerification = {
|
|
2135
|
-
...verified,
|
|
2136
|
-
verifiedAt: new Date().toISOString(),
|
|
2137
|
-
};
|
|
2138
2338
|
representativeRepairNodeIds.clear();
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2339
|
+
representativeRepairBehaviorByNode.clear();
|
|
2340
|
+
representativeEvidenceBehaviorByNode.clear();
|
|
2341
|
+
const checkedBehaviorIds = new Set(
|
|
2342
|
+
checks.map((check) => check.behaviorId),
|
|
2343
|
+
);
|
|
2143
2344
|
for (const behaviorResult of verified.behaviors) {
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2345
|
+
if (!checkedBehaviorIds.has(behaviorResult.behaviorId)) continue;
|
|
2346
|
+
const behaviorChecks = verified.checks.filter((check) =>
|
|
2347
|
+
check.behaviorId === behaviorResult.behaviorId);
|
|
2348
|
+
for (const check of behaviorChecks) {
|
|
2349
|
+
evidenceNodeIds([check]).forEach((nodeId) =>
|
|
2350
|
+
representativeEvidenceBehaviorByNode.set(
|
|
2351
|
+
nodeId,
|
|
2352
|
+
behaviorResult.behaviorId,
|
|
2353
|
+
));
|
|
2354
|
+
}
|
|
2355
|
+
const deterministicallyContradicted = behaviorChecks.some((check) =>
|
|
2356
|
+
check.deterministicVerdict === 'contradicted');
|
|
2357
|
+
const prior = behaviorVerificationAttempts.get(
|
|
2358
|
+
behaviorResult.behaviorId,
|
|
2359
|
+
) || {
|
|
2360
|
+
attempts: 0,
|
|
2361
|
+
unchangedEvidenceCount: 0,
|
|
2362
|
+
repairs: 0,
|
|
2363
|
+
fingerprint: null,
|
|
2364
|
+
exhausted: false,
|
|
2365
|
+
};
|
|
2366
|
+
if (behaviorResult.verdict === 'verified') {
|
|
2367
|
+
behaviorVerificationAttempts.delete(behaviorResult.behaviorId);
|
|
2368
|
+
Object.assign(behaviorResult, {
|
|
2369
|
+
verificationAttempts: prior.attempts + 1,
|
|
2370
|
+
verificationAttemptsRemaining: Math.max(
|
|
2371
|
+
0,
|
|
2372
|
+
MAX_INTERACTION_VERIFICATION_ATTEMPTS_PER_BEHAVIOR
|
|
2373
|
+
- prior.attempts
|
|
2374
|
+
- 1,
|
|
2375
|
+
),
|
|
2376
|
+
repairAttempts: prior.repairs,
|
|
2377
|
+
budgetExhausted: false,
|
|
2378
|
+
});
|
|
2150
2379
|
continue;
|
|
2151
2380
|
}
|
|
2152
|
-
const
|
|
2381
|
+
const evidenceFingerprint = crypto
|
|
2153
2382
|
.createHash('sha256')
|
|
2154
2383
|
.update(JSON.stringify({
|
|
2155
2384
|
mechanism: behaviorResult.mechanism,
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2385
|
+
verdict: behaviorResult.verdict,
|
|
2386
|
+
contradictions: [...behaviorResult.contradictions].sort(),
|
|
2387
|
+
unknowns: [...behaviorResult.unknowns].sort(),
|
|
2388
|
+
checks: behaviorChecks.map((check) => ({
|
|
2389
|
+
verdict: check.verdict,
|
|
2390
|
+
nodeIds: evidenceNodeIds([check]).sort(),
|
|
2391
|
+
observations: check.observations,
|
|
2392
|
+
})),
|
|
2159
2393
|
}))
|
|
2160
2394
|
.digest('hex');
|
|
2161
|
-
const
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
prior?.fingerprint === failureFingerprint
|
|
2166
|
-
? prior.count + 1
|
|
2395
|
+
const attempts = prior.attempts + 1;
|
|
2396
|
+
const unchangedEvidenceCount =
|
|
2397
|
+
prior.fingerprint === evidenceFingerprint
|
|
2398
|
+
? prior.unchangedEvidenceCount + 1
|
|
2167
2399
|
: 1;
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2400
|
+
const exhausted =
|
|
2401
|
+
attempts >= MAX_INTERACTION_VERIFICATION_ATTEMPTS_PER_BEHAVIOR
|
|
2402
|
+
|| unchangedEvidenceCount >= 2
|
|
2403
|
+
|| (
|
|
2404
|
+
behaviorResult.verdict === 'contradicted'
|
|
2405
|
+
&& prior.repairs >= MAX_INTERACTION_REPAIRS_PER_BEHAVIOR
|
|
2406
|
+
);
|
|
2407
|
+
behaviorVerificationAttempts.set(behaviorResult.behaviorId, {
|
|
2408
|
+
attempts,
|
|
2409
|
+
unchangedEvidenceCount,
|
|
2410
|
+
repairs: prior.repairs,
|
|
2411
|
+
fingerprint: evidenceFingerprint,
|
|
2412
|
+
exhausted,
|
|
2413
|
+
});
|
|
2414
|
+
Object.assign(behaviorResult, {
|
|
2415
|
+
verificationAttempts: attempts,
|
|
2416
|
+
verificationAttemptsRemaining: Math.max(
|
|
2417
|
+
0,
|
|
2418
|
+
MAX_INTERACTION_VERIFICATION_ATTEMPTS_PER_BEHAVIOR - attempts,
|
|
2419
|
+
),
|
|
2420
|
+
repairAttempts: prior.repairs,
|
|
2421
|
+
budgetExhausted: exhausted,
|
|
2422
|
+
});
|
|
2423
|
+
for (const check of behaviorChecks) {
|
|
2424
|
+
check.repairAllowed =
|
|
2425
|
+
check.deterministicVerdict === 'contradicted'
|
|
2426
|
+
&& prior.repairs < MAX_INTERACTION_REPAIRS_PER_BEHAVIOR
|
|
2427
|
+
&& !exhausted;
|
|
2428
|
+
if (!check.repairAllowed) continue;
|
|
2429
|
+
evidenceNodeIds([check]).forEach((nodeId) => {
|
|
2430
|
+
representativeRepairNodeIds.add(nodeId);
|
|
2431
|
+
representativeRepairBehaviorByNode.set(
|
|
2432
|
+
nodeId,
|
|
2433
|
+
behaviorResult.behaviorId,
|
|
2434
|
+
);
|
|
2435
|
+
});
|
|
2436
|
+
}
|
|
2172
2437
|
if (
|
|
2173
|
-
|
|
2438
|
+
!deterministicallyContradicted
|
|
2439
|
+
|| !exhausted
|
|
2174
2440
|
|| !behaviorResult.mechanism
|
|
2175
|
-
|| behaviorResult.mechanism === 'native_effect'
|
|
2176
2441
|
) continue;
|
|
2177
2442
|
const alreadyRejected = taskPlan.rejectedMechanisms.some((rejected) =>
|
|
2178
2443
|
rejected.mechanism === behaviorResult.mechanism
|
|
@@ -2191,8 +2456,8 @@ export function createFramerAgentToolRuntime({
|
|
|
2191
2456
|
behaviorSignature: behaviorResult.behaviorSignature,
|
|
2192
2457
|
mechanism: behaviorResult.mechanism,
|
|
2193
2458
|
reason:
|
|
2194
|
-
`Repeated
|
|
2195
|
-
.flatMap((check) => check.
|
|
2459
|
+
`Repeated live evidence contradicted this mechanism: ${behaviorChecks
|
|
2460
|
+
.flatMap((check) => check.contradictions)
|
|
2196
2461
|
.join('; ')
|
|
2197
2462
|
.slice(0, 700)}`,
|
|
2198
2463
|
source: 'verification_failure',
|
|
@@ -2200,6 +2465,15 @@ export function createFramerAgentToolRuntime({
|
|
|
2200
2465
|
],
|
|
2201
2466
|
};
|
|
2202
2467
|
}
|
|
2468
|
+
const budgetExhaustedBehaviorIds = [...behaviorVerificationAttempts]
|
|
2469
|
+
.filter(([, attempt]) => attempt.exhausted)
|
|
2470
|
+
.map(([behaviorId]) => behaviorId);
|
|
2471
|
+
interactionVerification = {
|
|
2472
|
+
...verified,
|
|
2473
|
+
semanticVerifierMode,
|
|
2474
|
+
budgetExhaustedBehaviorIds,
|
|
2475
|
+
verifiedAt: new Date().toISOString(),
|
|
2476
|
+
};
|
|
2203
2477
|
verified.representativeBehaviorIds.forEach((behaviorId) =>
|
|
2204
2478
|
representativeVerifiedBehaviorIds.add(behaviorId));
|
|
2205
2479
|
if (interactionVerification.completed) {
|
|
@@ -2271,7 +2545,8 @@ export function createFramerAgentToolRuntime({
|
|
|
2271
2545
|
'Framer Agent changes are required.',
|
|
2272
2546
|
);
|
|
2273
2547
|
}
|
|
2274
|
-
|
|
2548
|
+
const mutatedRepresentativeBehaviorIds =
|
|
2549
|
+
requireTaskPlanForMutation({ canvas: true, changes });
|
|
2275
2550
|
const pagePath = normalizePagePath(rawArguments.pagePath);
|
|
2276
2551
|
result = await execCode(
|
|
2277
2552
|
[
|
|
@@ -2282,6 +2557,8 @@ export function createFramerAgentToolRuntime({
|
|
|
2282
2557
|
mutationCalls += 1;
|
|
2283
2558
|
applyChangesCalls += 1;
|
|
2284
2559
|
markMutation();
|
|
2560
|
+
mutatedRepresentativeBehaviorIds.forEach((behaviorId) =>
|
|
2561
|
+
representativeMutatedBehaviorIds.add(behaviorId));
|
|
2285
2562
|
const parsedResult = parseStructuredOutput(result.stdout);
|
|
2286
2563
|
mutationReceipts.push({
|
|
2287
2564
|
sequence: operationSequence,
|
|
@@ -2541,6 +2818,10 @@ export function createFramerAgentToolRuntime({
|
|
|
2541
2818
|
lowLevelWriteDisabled,
|
|
2542
2819
|
lowLevelWriteBlockedCalls,
|
|
2543
2820
|
instructionFallbacks,
|
|
2821
|
+
interactionVerificationAttemptLimit:
|
|
2822
|
+
MAX_INTERACTION_VERIFICATION_ATTEMPTS_PER_BEHAVIOR,
|
|
2823
|
+
interactionRepairLimit:
|
|
2824
|
+
MAX_INTERACTION_REPAIRS_PER_BEHAVIOR,
|
|
2544
2825
|
},
|
|
2545
2826
|
branch: branchChange,
|
|
2546
2827
|
lastInspectionAt,
|