@agent-native/core 0.207.1 → 0.207.2-nightly-20261013011333

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.
Files changed (51) hide show
  1. package/dist/agent/production-agent.js +2 -0
  2. package/dist/cli/design-connect.d.ts +6 -0
  3. package/dist/cli/design-connect.js +880 -9
  4. package/dist/client/action-query-cache.d.ts +3 -1
  5. package/dist/client/action-query-cache.js +628 -161
  6. package/dist/client/action-query-cache.session-test-fixture.d.ts +49 -0
  7. package/dist/client/action-query-cache.session-test-fixture.js +151 -0
  8. package/dist/client/action-query-cache.test-fixtures.d.ts +2 -0
  9. package/dist/client/action-query-cache.test-fixtures.js +56 -0
  10. package/dist/client/motion-text.css +3 -0
  11. package/dist/client/motion-text.d.ts +38 -0
  12. package/dist/client/motion-text.js +551 -0
  13. package/dist/client/org/hooks.d.ts +3 -1
  14. package/dist/client/org/hooks.js +8 -2
  15. package/dist/client/route-chunk-recovery.d.ts +2 -0
  16. package/dist/client/route-chunk-recovery.js +11 -0
  17. package/dist/client/use-action.d.ts +10 -2
  18. package/dist/client/use-action.js +81 -12
  19. package/dist/client/use-settings-shortcut.js +8 -3
  20. package/dist/collab/awareness.d.ts +2 -2
  21. package/dist/collab/routes.d.ts +1 -1
  22. package/dist/collab/struct-routes.d.ts +1 -1
  23. package/dist/extensions/fetch-tool.js +8 -2
  24. package/dist/extensions/routes.js +7 -3
  25. package/dist/extensions/web-content.js +17 -2
  26. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  27. package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
  28. package/dist/mcp/build-server.js +5 -0
  29. package/dist/observability/routes.d.ts +4 -4
  30. package/dist/progress/routes.d.ts +1 -1
  31. package/dist/resources/handlers.d.ts +1 -1
  32. package/dist/secrets/routes.d.ts +5 -5
  33. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  34. package/dist/server/agent-engine-default-model-route.d.ts +2 -2
  35. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  36. package/dist/server/realtime-token.d.ts +1 -1
  37. package/dist/server/transcribe-voice.d.ts +1 -1
  38. package/dist/server/visual-edit-bridge-capability.d.ts +26 -0
  39. package/dist/server/visual-edit-bridge-capability.js +233 -0
  40. package/dist/shared/motion-easing.d.ts +6 -0
  41. package/dist/shared/motion-easing.js +109 -0
  42. package/dist/shared/motion-source-node-id.d.ts +3 -0
  43. package/dist/shared/motion-source-node-id.js +12 -0
  44. package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
  45. package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +35 -36
  46. package/dist/triggers/actions/manage-automation.d.ts +5 -5
  47. package/dist/vite/client.js +2 -0
  48. package/dist/vite/motion-source-provenance-plugin.d.ts +3 -0
  49. package/dist/vite/motion-source-provenance-plugin.js +339 -0
  50. package/package.json +14 -5
  51. package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +35 -36
@@ -0,0 +1,233 @@
1
+ import crypto from "node:crypto";
2
+ import path from "node:path";
3
+ export const LOCALHOST_BRIDGE_OPERATION_CAPABILITY_FIELD = "bridgeOperationCapability";
4
+ const CAPABILITY_TTL_MS = 120_000;
5
+ const MAX_CLOCK_SKEW_MS = 30_000;
6
+ const MAX_CANONICAL_BODY_BYTES = 8 * 1024 * 1024;
7
+ const MAX_JSON_DEPTH = 64;
8
+ const CAPABILITY_DOMAIN = "agent-native:visual-edit-bridge-operation:v2\0";
9
+ const AUDIENCE_DOMAIN = "agent-native:visual-edit-bridge-audience:v1\0";
10
+ function base64UrlEncode(value) {
11
+ return (typeof value === "string" ? Buffer.from(value, "utf8") : value)
12
+ .toString("base64")
13
+ .replace(/\+/g, "-")
14
+ .replace(/\//g, "_")
15
+ .replace(/=+$/g, "");
16
+ }
17
+ function base64UrlDecode(value) {
18
+ if (!/^[A-Za-z0-9_-]+$/.test(value))
19
+ return null;
20
+ const decoded = Buffer.from(value.replace(/-/g, "+").replace(/_/g, "/") +
21
+ "=".repeat((4 - (value.length % 4)) % 4), "base64");
22
+ return base64UrlEncode(decoded) === value ? decoded : null;
23
+ }
24
+ function canonicalJson(value, depth = 0, root = false) {
25
+ if (depth > MAX_JSON_DEPTH) {
26
+ throw new Error("bridge capability body exceeds the JSON depth limit");
27
+ }
28
+ if (value === null)
29
+ return "null";
30
+ if (typeof value === "string" || typeof value === "boolean") {
31
+ return JSON.stringify(value);
32
+ }
33
+ if (typeof value === "number") {
34
+ if (!Number.isFinite(value)) {
35
+ throw new Error("bridge capability body contains a non-finite number");
36
+ }
37
+ return JSON.stringify(value);
38
+ }
39
+ if (Array.isArray(value)) {
40
+ if (Object.getPrototypeOf(value) !== Array.prototype) {
41
+ throw new Error("bridge capability body contains a non-JSON array");
42
+ }
43
+ const keys = Reflect.ownKeys(value);
44
+ if (keys.length !== value.length + 1 ||
45
+ keys.some((key) => typeof key !== "string" ||
46
+ (key !== "length" &&
47
+ (!/^(0|[1-9]\d*)$/.test(key) || Number(key) >= value.length)))) {
48
+ throw new Error("bridge capability body contains a non-JSON array");
49
+ }
50
+ const items = [];
51
+ for (let index = 0; index < value.length; index += 1) {
52
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
53
+ if (!descriptor || !("value" in descriptor)) {
54
+ throw new Error("bridge capability body contains a non-JSON array");
55
+ }
56
+ items.push(canonicalJson(descriptor.value, depth + 1));
57
+ }
58
+ return `[${items.join(",")}]`;
59
+ }
60
+ if (typeof value !== "object") {
61
+ throw new Error("bridge capability body contains a non-JSON value");
62
+ }
63
+ const prototype = Object.getPrototypeOf(value);
64
+ if (prototype !== Object.prototype && prototype !== null) {
65
+ throw new Error("bridge capability body contains a non-JSON object");
66
+ }
67
+ const keys = Reflect.ownKeys(value);
68
+ if (keys.some((key) => typeof key !== "string")) {
69
+ throw new Error("bridge capability body contains a non-JSON object");
70
+ }
71
+ const entries = keys
72
+ .filter((key) => !(root && key === LOCALHOST_BRIDGE_OPERATION_CAPABILITY_FIELD))
73
+ .sort()
74
+ .map((key) => {
75
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
76
+ if (!descriptor || !("value" in descriptor)) {
77
+ throw new Error("bridge capability body contains an accessor");
78
+ }
79
+ return `${JSON.stringify(key)}:${canonicalJson(descriptor.value, depth + 1)}`;
80
+ });
81
+ return `{${entries.join(",")}}`;
82
+ }
83
+ function canonicalBodyHash(body) {
84
+ if (body === null ||
85
+ typeof body !== "object" ||
86
+ Array.isArray(body) ||
87
+ (Object.getPrototypeOf(body) !== Object.prototype &&
88
+ Object.getPrototypeOf(body) !== null)) {
89
+ throw new Error("bridge capability body must be a JSON object");
90
+ }
91
+ const canonical = canonicalJson(body, 0, true);
92
+ if (Buffer.byteLength(canonical, "utf8") > MAX_CANONICAL_BODY_BYTES) {
93
+ throw new Error("bridge capability body exceeds the size limit");
94
+ }
95
+ return base64UrlEncode(crypto.createHash("sha256").update(canonical, "utf8").digest());
96
+ }
97
+ function assertOperation(operation) {
98
+ if (typeof operation !== "string" ||
99
+ operation.length === 0 ||
100
+ operation.length > 512) {
101
+ throw new Error("bridge capability operation is invalid");
102
+ }
103
+ }
104
+ function canonicalBridgeRootPath(rootPath) {
105
+ if (typeof rootPath !== "string" ||
106
+ rootPath.length === 0 ||
107
+ rootPath !== rootPath.trim() ||
108
+ rootPath.length > 32_768) {
109
+ throw new Error("bridge root path is invalid");
110
+ }
111
+ // Use the platform-independent path implementation for the path syntax so
112
+ // a cloud server and a local Windows CLI derive the same audience.
113
+ if ((/^[A-Za-z]:[\\/]/.test(rootPath) || rootPath.startsWith("\\\\")) &&
114
+ path.win32.isAbsolute(rootPath)) {
115
+ const normalized = path.win32.normalize(rootPath).replace(/\\/g, "/");
116
+ const canonicalRoot = path.win32.parse(rootPath).root.replace(/\\/g, "/");
117
+ return normalized.length > canonicalRoot.length
118
+ ? normalized.replace(/\/+$/, "")
119
+ : normalized;
120
+ }
121
+ if (path.posix.isAbsolute(rootPath)) {
122
+ const normalized = path.posix.normalize(rootPath);
123
+ return normalized === "/" ? normalized : normalized.replace(/\/+$/, "");
124
+ }
125
+ throw new Error("bridge root path must be absolute");
126
+ }
127
+ export function createLocalhostBridgeAudience(rootPath) {
128
+ return crypto
129
+ .createHash("sha256")
130
+ .update(AUDIENCE_DOMAIN, "utf8")
131
+ .update(canonicalBridgeRootPath(rootPath), "utf8")
132
+ .digest("base64url");
133
+ }
134
+ function signPayload(bridgeToken, encodedPayload) {
135
+ return base64UrlEncode(crypto
136
+ .createHmac("sha256", bridgeToken)
137
+ .update(CAPABILITY_DOMAIN, "utf8")
138
+ .update(encodedPayload, "ascii")
139
+ .digest());
140
+ }
141
+ export function issueLocalhostBridgeOperationCapability({ bridgeToken, audience, operation, body, issuedAt = Date.now(), }) {
142
+ if (typeof bridgeToken !== "string" || bridgeToken.length === 0) {
143
+ throw new Error("bridge token is required");
144
+ }
145
+ if (!/^[A-Za-z0-9_-]{43}$/.test(audience)) {
146
+ throw new Error("bridge capability audience is invalid");
147
+ }
148
+ assertOperation(operation);
149
+ if (!Number.isSafeInteger(issuedAt) || issuedAt <= 0) {
150
+ throw new Error("bridge capability issuance time is invalid");
151
+ }
152
+ const claims = {
153
+ version: 2,
154
+ audience,
155
+ operation,
156
+ issuedAt,
157
+ expiresAt: issuedAt + CAPABILITY_TTL_MS,
158
+ nonce: base64UrlEncode(crypto.randomBytes(32)),
159
+ bodyHash: canonicalBodyHash(body),
160
+ };
161
+ if (!Number.isSafeInteger(claims.expiresAt)) {
162
+ throw new Error("bridge capability expiration time is invalid");
163
+ }
164
+ const encodedPayload = base64UrlEncode(JSON.stringify(claims));
165
+ return {
166
+ capability: `${encodedPayload}.${signPayload(bridgeToken, encodedPayload)}`,
167
+ nonce: claims.nonce,
168
+ expiresAt: claims.expiresAt,
169
+ };
170
+ }
171
+ export function verifyLocalhostBridgeOperationCapability({ bridgeToken, audience, operation, body, capability, now = Date.now(), }) {
172
+ try {
173
+ if (typeof bridgeToken !== "string" ||
174
+ bridgeToken.length === 0 ||
175
+ typeof capability !== "string" ||
176
+ capability.length > 4096 ||
177
+ !/^[A-Za-z0-9_-]{43}$/.test(audience) ||
178
+ !Number.isSafeInteger(now)) {
179
+ return null;
180
+ }
181
+ assertOperation(operation);
182
+ const segments = capability.split(".");
183
+ if (segments.length !== 2)
184
+ return null;
185
+ const payloadBytes = base64UrlDecode(segments[0] ?? "");
186
+ const signature = base64UrlDecode(segments[1] ?? "");
187
+ if (!payloadBytes || !signature || signature.length !== 32)
188
+ return null;
189
+ const expectedSignature = base64UrlDecode(signPayload(bridgeToken, segments[0] ?? ""));
190
+ if (!expectedSignature ||
191
+ !crypto.timingSafeEqual(signature, expectedSignature)) {
192
+ return null;
193
+ }
194
+ const claims = JSON.parse(payloadBytes.toString("utf8"));
195
+ if (claims.version !== 2 ||
196
+ claims.audience !== audience ||
197
+ claims.operation !== operation ||
198
+ !Number.isSafeInteger(claims.issuedAt) ||
199
+ !Number.isSafeInteger(claims.expiresAt) ||
200
+ typeof claims.nonce !== "string" ||
201
+ typeof claims.bodyHash !== "string" ||
202
+ claims.expiresAt !== claims.issuedAt + CAPABILITY_TTL_MS ||
203
+ claims.issuedAt > now + MAX_CLOCK_SKEW_MS ||
204
+ claims.expiresAt <= now ||
205
+ !/^[A-Za-z0-9_-]{43}$/.test(claims.nonce) ||
206
+ !/^[A-Za-z0-9_-]{43}$/.test(claims.bodyHash)) {
207
+ return null;
208
+ }
209
+ const allowedKeys = [
210
+ "version",
211
+ "audience",
212
+ "operation",
213
+ "issuedAt",
214
+ "expiresAt",
215
+ "nonce",
216
+ "bodyHash",
217
+ ];
218
+ if (Object.keys(claims).length !== allowedKeys.length ||
219
+ Object.keys(claims).some((key) => !allowedKeys.includes(key))) {
220
+ return null;
221
+ }
222
+ if (canonicalBodyHash(body) !== claims.bodyHash)
223
+ return null;
224
+ return {
225
+ nonce: claims.nonce,
226
+ expiresAt: claims.expiresAt,
227
+ };
228
+ // coercion-ok: malformed capability claims are an explicit authorization denial; null never grants access.
229
+ }
230
+ catch {
231
+ return null;
232
+ }
233
+ }
@@ -0,0 +1,6 @@
1
+ export declare function motionEaseVariableName(ease: string): string | null;
2
+ export declare function isMotionEaseVariableReference(ease: string): boolean;
3
+ /** True only for timing functions accepted by the Motion authoring model. */
4
+ export declare function isValidMotionEase(ease: string): boolean;
5
+ /** Resolves Motion variables and spring tokens to easing strings accepted by WAAPI. */
6
+ export declare function resolveMotionEaseForWebAnimations(ease: string, readCssVariable: (name: string) => string): string;
@@ -0,0 +1,109 @@
1
+ const CSS_NUMBER = String.raw `[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?`;
2
+ const CSS_PERCENTAGE = String.raw `${CSS_NUMBER}%`;
3
+ const MOTION_EASING_VARIABLE_REFERENCE_RE = /^var\(--an-motion-ease-([a-z0-9][a-z0-9-]{0,63})\)$/;
4
+ const SPRING_EASING_RE = /^spring\(\s*([+-]?(?:\d+\.?\d*|\.\d+))\s*(?:,\s*([+-]?(?:\d+\.?\d*|\.\d+))\s*)?\)$/i;
5
+ export function motionEaseVariableName(ease) {
6
+ const match = MOTION_EASING_VARIABLE_REFERENCE_RE.exec(ease);
7
+ return match ? `--an-motion-ease-${match[1]}` : null;
8
+ }
9
+ export function isMotionEaseVariableReference(ease) {
10
+ return motionEaseVariableName(ease) !== null;
11
+ }
12
+ /** True only for timing functions accepted by the Motion authoring model. */
13
+ export function isValidMotionEase(ease) {
14
+ if (isMotionEaseVariableReference(ease))
15
+ return true;
16
+ const value = ease.trim();
17
+ const keyword = value.toLowerCase();
18
+ if ([
19
+ "linear",
20
+ "ease",
21
+ "ease-in",
22
+ "ease-out",
23
+ "ease-in-out",
24
+ "step-start",
25
+ "step-end",
26
+ ].includes(keyword)) {
27
+ return true;
28
+ }
29
+ const spring = SPRING_EASING_RE.exec(value);
30
+ if (keyword === "spring")
31
+ return true;
32
+ if (spring) {
33
+ const bounce = Number(spring[1]);
34
+ const settle = spring[2] === undefined ? 1 : Number(spring[2]);
35
+ return bounce >= 0 && bounce <= 1 && settle >= 0.05 && settle <= 1;
36
+ }
37
+ if (/^spring/i.test(value))
38
+ return false;
39
+ const bezier = new RegExp(`^cubic-bezier\\(\\s*(${CSS_NUMBER})\\s*,\\s*(${CSS_NUMBER})\\s*,\\s*(${CSS_NUMBER})\\s*,\\s*(${CSS_NUMBER})\\s*\\)$`, "i").exec(value);
40
+ if (bezier) {
41
+ const [, x1, , x2] = bezier;
42
+ return (Number(x1) >= 0 && Number(x1) <= 1 && Number(x2) >= 0 && Number(x2) <= 1);
43
+ }
44
+ const steps = /^steps\(\s*(\d+)\s*(?:,\s*(jump-start|jump-end|jump-none|jump-both|start|end)\s*)?\)$/i.exec(value);
45
+ if (steps) {
46
+ const count = Number(steps[1]);
47
+ const position = steps[2]?.toLowerCase();
48
+ return (count > 0 &&
49
+ Number.isSafeInteger(count) &&
50
+ (position === "jump-none" ? count > 1 : true));
51
+ }
52
+ const linear = /^linear\((.*)\)$/i.exec(value);
53
+ if (!linear)
54
+ return false;
55
+ const stops = linear[1].split(",").map((stop) => stop.trim());
56
+ if (stops.length < 2)
57
+ return false;
58
+ return stops.every((stop) => new RegExp(`^${CSS_NUMBER}(?:\\s+${CSS_PERCENTAGE}(?:\\s+${CSS_PERCENTAGE})?)?$`, "i").test(stop));
59
+ }
60
+ /** Resolves Motion variables and spring tokens to easing strings accepted by WAAPI. */
61
+ export function resolveMotionEaseForWebAnimations(ease, readCssVariable) {
62
+ const variableName = motionEaseVariableName(ease);
63
+ let resolvedEase = ease;
64
+ if (variableName) {
65
+ resolvedEase = readCssVariable(variableName).trim();
66
+ if (!resolvedEase ||
67
+ isMotionEaseVariableReference(resolvedEase) ||
68
+ !isValidMotionEase(resolvedEase)) {
69
+ throw new TypeError(`Motion easing variable "${variableName}" is not defined or does not resolve to a concrete easing.`);
70
+ }
71
+ }
72
+ if (!isValidMotionEase(resolvedEase)) {
73
+ throw new TypeError(`"${resolvedEase}" is not a supported motion easing value.`);
74
+ }
75
+ const normalized = resolvedEase.trim();
76
+ if (normalized.toLowerCase() === "spring") {
77
+ return springToCssLinear(0.25, 1);
78
+ }
79
+ const spring = SPRING_EASING_RE.exec(normalized);
80
+ if (!spring)
81
+ return resolvedEase;
82
+ return springToCssLinear(Number(spring[1]), Number(spring[2] ?? 1));
83
+ }
84
+ function springToCssLinear(bounce, settle) {
85
+ const sampleCount = 16 + Math.round(bounce * 34);
86
+ const stops = Array.from({ length: sampleCount + 1 }, (_, index) => {
87
+ const x = index / sampleCount;
88
+ const value = index === sampleCount ? 1 : sampleSpring(bounce, settle, x);
89
+ return (Math.round(value * 10_000) / 10_000).toString();
90
+ });
91
+ return `linear(${stops.join(", ")})`;
92
+ }
93
+ function sampleSpring(bounce, settle, x) {
94
+ if (x <= 0)
95
+ return 0;
96
+ const u = x / settle;
97
+ if (u >= 1)
98
+ return 1;
99
+ const zeta = Math.min(1, Math.max(0.02, 1 - bounce));
100
+ const omega = Math.log(1 / 0.001) / zeta;
101
+ if (zeta >= 1) {
102
+ return 1 - Math.exp(-omega * u) * (1 + omega * u);
103
+ }
104
+ const omegaD = omega * Math.sqrt(1 - zeta * zeta);
105
+ const decay = Math.exp(-zeta * omega * u);
106
+ return (1 -
107
+ decay *
108
+ (Math.cos(omegaD * u) + ((zeta * omega) / omegaD) * Math.sin(omegaD * u)));
109
+ }
@@ -0,0 +1,3 @@
1
+ /** Stable ID for a host JSX element within its project-relative source module. */
2
+ export declare function generatedMotionNodeId(modulePath: string, hostElementOrdinal: number): string;
3
+ export declare function isMotionSourceHostElementTag(tagName: string): boolean;
@@ -0,0 +1,12 @@
1
+ /** Stable ID for a host JSX element within its project-relative source module. */
2
+ export function generatedMotionNodeId(modulePath, hostElementOrdinal) {
3
+ let hash = 0xcbf29ce484222325n;
4
+ for (let index = 0; index < modulePath.length; index += 1) {
5
+ hash ^= BigInt(modulePath.charCodeAt(index));
6
+ hash = BigInt.asUintN(64, hash * 0x100000001b3n);
7
+ }
8
+ return `an-${hash.toString(16).padStart(16, "0")}-${hostElementOrdinal}`;
9
+ }
10
+ export function isMotionSourceHostElementTag(tagName) {
11
+ return /^[a-z]/.test(tagName) && tagName !== "script" && tagName !== "style";
12
+ }
@@ -2,6 +2,7 @@ declare const _default: import("../../action.js").ActionDefinition<{
2
2
  resourceType: string;
3
3
  resourceId: string;
4
4
  }, {
5
+ role?: undefined;
5
6
  ownerEmail: any;
6
7
  visibility: any;
7
8
  shares: any[];
@@ -11,7 +12,6 @@ declare const _default: import("../../action.js").ActionDefinition<{
11
12
  supportsGroupShares?: boolean;
12
13
  };
13
14
  orgId?: undefined;
14
- role?: undefined;
15
15
  agentReadable?: undefined;
16
16
  } | {
17
17
  ownerEmail: string;
@@ -19,7 +19,7 @@ Four phases, in order. Phase 0 comes before any investigation, not after.
19
19
  before investigating any of it.
20
20
  1. **Answer the people who answered you.** Older open questions first.
21
21
  2. **Fix** what the evidence actually proves, at the owning boundary.
22
- 3. **Reply**, under a hard question budget, then recap.
22
+ 3. **Reply** with useful questions, then recap.
23
23
 
24
24
  Output is fixes. Reply in-thread for every workflow reaction added this run;
25
25
  continuing work gets a concrete progress reply, and resolved work gets a final
@@ -82,9 +82,13 @@ error message, URL, or visible flow is enough to start tracing its owner; don't
82
82
  wait for details that would not change the reproduction attempt. Phase 2's
83
83
  local reproduction procedure applies to all defect reports, not only bashes.
84
84
 
85
- If no safe repo-owned fix is evident after that attempt, record the specific
86
- evidence limit. Ask only a question whose answer could change the reproduction
87
- or unblock a fix; after four days without an answer, record **Abandoned - no
85
+ If no safe repo-owned fix is evident after reproduction, record the evidence
86
+ limit. For each defect where reporter input changes reproduction or unblocks a
87
+ fix, ask one direct question naming the missing detail or action and why it
88
+ gates the next step. Inspect supplied run IDs, logs, links, and artifacts
89
+ yourself. Keep internal access, config, observability, and deployment gaps
90
+ internal; never ask reporters for developer traces. Do not label the thread
91
+ **Open** instead of asking. After four days unanswered, record **Abandoned - no
88
92
  answer in 4 days**.
89
93
 
90
94
  Use **Skipped** only for non-defects, never breakage. **Open - no question**
@@ -448,9 +452,11 @@ instruction or prompt exception.
448
452
 
449
453
  ## Phase 3: reply
450
454
 
451
- Thank issue reporters for opening it and Slack reporters for sharing. Give the
452
- status or ask a useful question. Follow `address-feedback-with-replies` for
453
- Slack voice. End each Slack reply with `this was sent from a bot.` after its
455
+ Thank issue reporters for opening it and Slack reporters for sharing. Give an
456
+ active status while work continues. If reporter input gates a fix, ask the
457
+ direct question specified in Phase 0; do not substitute **Open**, **pending**,
458
+ or an evidence description. Follow `address-feedback-with-replies` for Slack
459
+ voice. End each Slack reply with `this was sent from a bot.` after its
454
460
  plain-language status.
455
461
 
456
462
  Short replies: say the change, limits, and next step. Link merged PRs and say
@@ -474,32 +480,26 @@ needed. Do not reopen Slack in later runs to reply.
474
480
  observations may be silent; merged Slack fixes still get the reply above.
475
481
  **Shipped** is for upvoted improvements.
476
482
  - **In progress** — name continuing work and its owner; ask nothing.
477
- - **A question** — subject to the budget below.
483
+ - **A question** — follow the rules below.
478
484
 
479
- Unclaimed scope/noise gets an internal recap row. Ask about an unverified defect
480
- only when one answer would unblock it; otherwise record **Open - no question**
481
- once. Cluster duplicates. Before re-reading any thread to reply, apply the Slack
482
- ownership gate; stay out of marked messages and active human work.
485
+ Unclaimed scope/noise gets an internal recap row. Cluster duplicates. Before
486
+ replying, apply the Slack ownership gate; stay out of marked messages and active
487
+ human work.
483
488
 
484
- ### The question budget
489
+ ### Ask only answerable questions
485
490
 
486
- **At most three questions per run, across all sources.** Most runs ask zero or
487
- one.
491
+ Ask one direct question for each distinct blocked defect when the reporter's
492
+ answer or action changes reproduction or unblocks the fix. There is no per-run
493
+ cap; do not replace a useful question with **Open** or an in-progress status.
494
+ Keep one pending question per thread. After an answer, try the fix before asking
495
+ anything else. Never ask for supplied logs/IDs, internal access/config/tooling,
496
+ subjective choices, or details that do not change the next step. If a
497
+ user-visible link or ID alone blocks reproduction, ask for it and say where to
498
+ find it.
488
499
 
489
- So rank before you ask. For each candidate, state: *if I get this answer, I
490
- can ship the fix.* Ask the three with the strongest answer. If fewer than
491
- three clear that bar, ask fewer. Everything below the cut is an internal open
492
- item, not a message.
493
-
494
- Never ask for supplied/inspectable evidence, irrelevant IDs/build numbers,
495
- subjective choices, or internal blockers. If a user-visible link or ID is the
496
- sole blocker, ask plainly and say where to find it.
497
-
498
- At most one clarification question may be pending per thread at a time. Once it
499
- is answered or resolved, attempt the fix; if that exposes a different required
500
- detail, ask at most one new, non-repeating question. Never stack questions or
501
- repeat a pending one. If a needed artifact is inaccessible to you, ask for a
502
- fresh link - not for its contents again.
500
+ If an answer exposes a different required detail, ask at most one new,
501
+ non-repeating question. If an artifact is inaccessible, request a fresh link,
502
+ not its contents.
503
503
 
504
504
  ## Verification and identity
505
505
 
@@ -563,7 +563,7 @@ Messages: product N · QA N · dev N (total N)
563
563
  Deployment cursor: <timestamp> · carried active run IDs/rows: <ids/count>
564
564
  Release lanes: N inspected · failed/stale N · target verified N
565
565
  Reaction-gated skips: N · claimed N · answered N
566
- Questions asked: N/3 · Dropped at 4 days: N
566
+ Questions asked: N · Dropped at 4 days: N
567
567
  Repeats of a prior Fixed claim: N (each with its earlier thread and failed fix)
568
568
  Upvoted items in scope: N (built: N)
569
569
 
@@ -576,12 +576,11 @@ Tracker: <sheet/export and bounded range> - N rows enumerated, N ledgers complet
576
576
  Unavailable or unverified: ...
577
577
  ```
578
578
 
579
- `Open - no question` is a last resort, not a success state. It requires that
580
- you worked the defect, could not fix it, and could not form a question that would
581
- unblock it; a run whose ledger is mostly `Open - no question` has under-asked, not
582
- finished. It keeps our eye and has no checkmark. "Nothing
583
- matched" is valid only after each source was queried successfully, with the
584
- cursor stated.
579
+ `Open - no question` follows reproduction when there is no safe fix and no
580
+ reporter answer or action that changes the next step. Ask when reporter input
581
+ unblocks the fix; keep internal access/tooling blockers internal. Mostly open
582
+ ledgers mean under-asking. Keep `👀`, no `✅`. "Nothing matched" is valid only
583
+ after every source was queried successfully and the cursor is stated.
585
584
 
586
585
  ## Related skills
587
586
 
@@ -13,8 +13,6 @@ declare const _default: import("../../action.js").ActionDefinition<{
13
13
  executionEngine?: string;
14
14
  executionCwd?: string;
15
15
  }, {
16
- deleted?: undefined;
17
- enabled?: undefined;
18
16
  created: boolean;
19
17
  name: string;
20
18
  scope: "organization" | "personal";
@@ -25,31 +23,33 @@ declare const _default: import("../../action.js").ActionDefinition<{
25
23
  reasoningEffort: "auto" | "high" | "low" | "max" | "medium" | "minimal" | "none" | "xhigh";
26
24
  webhookPath: string;
27
25
  nextRun: string;
26
+ deleted?: undefined;
27
+ enabled?: undefined;
28
28
  executionHostId?: undefined;
29
29
  executionEngine?: undefined;
30
30
  executionCwd?: undefined;
31
31
  } | {
32
32
  scope?: undefined;
33
- enabled?: undefined;
34
- nextRun?: undefined;
35
33
  created?: undefined;
36
34
  triggerType?: undefined;
37
35
  event?: undefined;
38
36
  deleted: boolean;
39
37
  name: string;
38
+ enabled?: undefined;
40
39
  schedule?: undefined;
41
40
  timezone?: undefined;
42
41
  reasoningEffort?: undefined;
43
42
  executionHostId?: undefined;
44
43
  executionEngine?: undefined;
45
44
  executionCwd?: undefined;
45
+ nextRun?: undefined;
46
46
  webhookPath?: undefined;
47
47
  } | {
48
48
  scope?: undefined;
49
- deleted?: undefined;
50
49
  created?: undefined;
51
50
  triggerType?: undefined;
52
51
  event?: undefined;
52
+ deleted?: undefined;
53
53
  name: string;
54
54
  enabled: boolean;
55
55
  schedule: string;
@@ -29,6 +29,7 @@ import { actionTypesPlugin } from "./action-types-plugin.js";
29
29
  import { createAgentNativeConfigContext, loadAgentNativeConfigFile, loadWorkspaceAgentNativeConfigFile, readAgentNativeJsonConfig, resolveFirstRunOnboardingBuildReplacement, resolveHarnessBuildReplacement, writeAgentNativeBuildConfigMarker, } from "./agent-native-config-loader.js";
30
30
  import { agentsBundlePlugin } from "./agents-bundle-plugin.js";
31
31
  import { migrationDiagnosticPlugin } from "./migration-diagnostic-plugin.js";
32
+ import { motionSourceProvenancePlugin } from "./motion-source-provenance-plugin.js";
32
33
  import { resolveAgentNativePackageVersions } from "./package-versions.js";
33
34
  import { createSentrySourceMapUploadPlugin, isSentrySourceMapUploadEnabled, } from "./sentry-source-maps.js";
34
35
  const require = createRequire(import.meta.url);
@@ -3603,6 +3604,7 @@ function createAgentNativePlugins(options, { command, includeReactTransform, use
3603
3604
  : [];
3604
3605
  return [
3605
3606
  migrationDiagnosticPlugin(),
3607
+ motionSourceProvenancePlugin(),
3606
3608
  persistent5xxRecovery(),
3607
3609
  presetMarkerPlugin,
3608
3610
  ssrStubPlugin([
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from "vite";
2
+ /** Adds dev-only JSX identity evidence so source edits follow rendered nodes. */
3
+ export declare function motionSourceProvenancePlugin(): Plugin;