@wenathlan/extension 1.1.30 → 1.1.31

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 CHANGED
@@ -2,17 +2,19 @@
2
2
 
3
3
  Devthink is a **consent-first browser-agent bridge** distributed as a TypeScript library and a Chromium Manifest V3 extension. It turns a user-provided browser objective into a bounded, reviewable plan. The user must start the active-tab session and approve the plan before any page action reaches the browser.
4
4
 
5
- Version: **1.1.30**. License: **GPL-3.0-only**. The repository is `wenathlan/extension`; the npm-compatible scoped package identifier is `@wenathlan/extension`.
5
+ Version: **1.1.31**. License: **GPL-3.0-only**. The repository is `wenathlan/extension`; the npm-compatible scoped package identifier is `@wenathlan/extension`.
6
6
 
7
7
  ## What it does
8
8
 
9
- | Capability | Behavior in 1.1.30 |
9
+ | Capability | Behavior in 1.1.31 |
10
10
  | --- | --- |
11
11
  | Active-tab session | The user starts a short-lived session for one HTTPS tab and one origin. |
12
12
  | Page observation | The extension captures a bounded semantic inventory of interactive elements and form labels. |
13
13
  | Plan proposal | A local plan can be created immediately; an optional user-configured HTTPS endpoint can return a typed plan proposal. |
14
14
  | Review gate | Every remote proposal starts in `pending`; it cannot reach the page bridge before explicit approval. |
15
- | Browser tools | Reviewed plans may observe, inspect, focus, click, type approved text or navigate within the same approved origin. |
15
+ | Browser tools | Reviewed plans may observe, inspect, focus, click, type approved text, scroll, select an existing option, hover, extract bounded text or links, wait a bounded pause or navigate within the same approved origin. |
16
+ | Session pause | The user can pause and resume the active session; a paused session blocks every execution and preview while keeping the reviewed plan alive. |
17
+ | Plan progress | Each reviewed step is tracked, completed steps are marked in the side panel and the approved plan closes automatically once every step has executed. |
16
18
  | Stop and audit | The user can stop the session at any time. Configuration, proposals, decisions and results remain in local extension storage. |
17
19
 
18
20
  The project is deliberately **not** a hosted control platform. It does not depend on a provider-specific sandbox, server URL or browser profile. A configured endpoint is optional, has to use HTTPS and receives only the session record and bounded observation required to produce a plan.
@@ -40,7 +42,7 @@ The endpoint field intentionally has no default URL. Enter a URL such as `https:
40
42
 
41
43
  ```json
42
44
  {
43
- "version": "1.1.30",
45
+ "version": "1.1.31",
44
46
  "objective": "User supplied objective",
45
47
  "session": { "id": "uuid", "tabid": 1, "origin": "https://example.com" },
46
48
  "observation": { "url": "https://example.com/path", "interactive": [] }
package/dist/index.js CHANGED
@@ -28,6 +28,12 @@ var sessionmemory = class {
28
28
  async setdiagnostic(value) {
29
29
  return this.adapter.set("diagnostic", value);
30
30
  }
31
+ async getprogress() {
32
+ return this.adapter.get("progress");
33
+ }
34
+ async setprogress(value) {
35
+ return this.adapter.set("progress", value);
36
+ }
31
37
  async getaudit() {
32
38
  return await this.adapter.get("audit") ?? [];
33
39
  }
@@ -41,8 +47,10 @@ function randomid() {
41
47
  }
42
48
 
43
49
  // policy.ts
44
- var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate"]);
45
- var allowedactions = /* @__PURE__ */ new Set(["observe", "inspect", "focus", "click", "type", "navigate"]);
50
+ var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select"]);
51
+ var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover"]);
52
+ var allowedactions = /* @__PURE__ */ new Set(["observe", "inspect", "focus", "click", "type", "navigate", "scroll", "select", "hover", "extract", "wait"]);
53
+ var targetactions = /* @__PURE__ */ new Set(["inspect", "focus", "click", "type", "scroll", "select", "hover"]);
46
54
  function normalizeendpoint(value) {
47
55
  const endpoint = new URL(value.trim());
48
56
  if (endpoint.protocol !== "https:") throw new Error("Devthink accepts HTTPS endpoints only.");
@@ -57,12 +65,25 @@ function hostpattern(origin) {
57
65
  function actionrisk(kind) {
58
66
  if (!allowedactions.has(kind)) throw new Error("Unsupported browser action.");
59
67
  if (sensitiveactions.has(kind)) return "sensitive";
60
- return kind === "focus" ? "interaction" : "read";
68
+ return interactionactions.has(kind) ? "interaction" : "read";
69
+ }
70
+ function waitduration(step) {
71
+ const requested = step.value ? Number.parseInt(step.value, 10) : 250;
72
+ if (!Number.isFinite(requested) || requested < 0) throw new Error("Wait duration must be zero or a positive number of milliseconds.");
73
+ return Math.min(requested, 1e4);
61
74
  }
62
75
  function validatestep(step, origin) {
63
76
  if (!allowedactions.has(step.kind)) return { allowed: false, reason: "Unsupported action kind." };
64
77
  if (!step.summary.trim()) return { allowed: false, reason: "A human-readable action summary is required." };
65
- if ((step.kind === "click" || step.kind === "focus" || step.kind === "inspect" || step.kind === "type") && !step.target?.trim()) return { allowed: false, reason: "A page target is required." };
78
+ if (targetactions.has(step.kind) && !step.target?.trim()) return { allowed: false, reason: "A page target is required." };
79
+ if (step.kind === "select" && !step.value?.trim()) return { allowed: false, reason: "A reviewed option value is required." };
80
+ if (step.kind === "wait") {
81
+ try {
82
+ waitduration(step);
83
+ } catch {
84
+ return { allowed: false, reason: "Wait duration must be zero or a positive number of milliseconds." };
85
+ }
86
+ }
66
87
  if (step.kind === "navigate") {
67
88
  if (!step.value) return { allowed: false, reason: "A navigation URL is required." };
68
89
  try {
@@ -73,18 +94,24 @@ function validatestep(step, origin) {
73
94
  }
74
95
  return { allowed: true };
75
96
  }
97
+ function sessiongate(input) {
98
+ if (!input.session || input.session.stoppedat) return { allowed: false, reason: "No active browser session exists." };
99
+ if (input.session.expiresat <= input.now) return { allowed: false, reason: "The browser session has expired." };
100
+ if (input.session.pausedat) return { allowed: false, reason: `The browser session is paused and cannot ${input.action}.` };
101
+ if (input.session.tabid !== input.tabid || input.session.origin !== input.origin) return { allowed: false, reason: `The ${input.action} is outside the approved tab or origin.` };
102
+ return { allowed: true };
103
+ }
76
104
  function canexecute(input) {
77
105
  const now = input.now ?? Date.now();
78
- if (!input.session || input.session.stoppedat) return { allowed: false, reason: "No active browser session exists." };
79
- if (input.session.expiresat <= now) return { allowed: false, reason: "The browser session has expired." };
80
- if (input.session.tabid !== input.tabid || input.session.origin !== input.origin) return { allowed: false, reason: "The action is outside the approved tab or origin." };
106
+ const gate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: "execute an action" });
107
+ if (!gate.allowed) return gate;
81
108
  if (!input.plan || input.plan.state !== "approved") return { allowed: false, reason: "The plan has not received explicit approval." };
82
109
  if (input.plan.expiresat <= now) return { allowed: false, reason: "The approved plan has expired." };
83
110
  return validatestep(input.step, input.origin);
84
111
  }
85
112
 
86
113
  // version.ts
87
- var packageversion = "1.1.30";
114
+ var packageversion = "1.1.31";
88
115
 
89
116
  // types.ts
90
117
  var protocolversion = packageversion;
@@ -103,7 +130,7 @@ function parseproposal(value, origin) {
103
130
  if (root.version !== protocolversion) throw new Error("Unsupported protocol version.");
104
131
  const planinput = record(root.plan);
105
132
  const stepsinput = planinput.steps;
106
- if (!Array.isArray(stepsinput) || stepsinput.length === 0 || stepsinput.length > 20) throw new Error("A plan needs between one and twenty steps.");
133
+ if (!Array.isArray(stepsinput) || stepsinput.length === 0 || stepsinput.length > 50) throw new Error("A plan needs between one and fifty steps.");
107
134
  const steps = stepsinput.map((input, index) => {
108
135
  const candidate = record(input);
109
136
  const kind = text(candidate.kind, `step ${index + 1} kind`);
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../memory.ts", "../policy.ts", "../version.ts", "../types.ts", "../protocol.ts"],
4
- "sourcesContent": ["import type { agentplan, agentsession, auditevent, diagnosticreport, endpointconfig } from \"./types.js\";\n\n/** Provides a small storage seam that works in browser, tests and future adapters. */\nexport interface memoryadapter {\n get<T>(key: string): Promise<T | undefined>;\n set<T>(key: string, value: T): Promise<void>;\n}\n\nexport class sessionmemory {\n constructor(private readonly adapter: memoryadapter) {}\n\n async getconfig(): Promise<endpointconfig | undefined> { return this.adapter.get<endpointconfig>(\"config\"); }\n async setconfig(value: endpointconfig): Promise<void> { return this.adapter.set(\"config\", value); }\n async getsession(): Promise<agentsession | undefined> { return this.adapter.get<agentsession>(\"session\"); }\n async setsession(value: agentsession): Promise<void> { return this.adapter.set(\"session\", value); }\n async getplan(): Promise<agentplan | undefined> { return this.adapter.get<agentplan>(\"plan\"); }\n async setplan(value: agentplan): Promise<void> { return this.adapter.set(\"plan\", value); }\n async getdiagnostic(): Promise<diagnosticreport | undefined> { return this.adapter.get<diagnosticreport>(\"diagnostic\"); }\n async setdiagnostic(value: diagnosticreport): Promise<void> { return this.adapter.set(\"diagnostic\", value); }\n async getaudit(): Promise<auditevent[]> { return (await this.adapter.get<auditevent[]>(\"audit\")) ?? []; }\n async addaudi(event: auditevent): Promise<void> {\n const records = await this.getaudit();\n await this.adapter.set(\"audit\", [event, ...records].slice(0, 100));\n }\n}\n\n/** Creates identifiers locally without a network dependency. */\nexport function randomid(): string {\n return crypto.randomUUID();\n}\n", "import type { actionkind, agentplan, agentsession, endpointconfig, policyevaluation, toolstep } from \"./types.js\";\n\nconst sensitiveactions = new Set<actionkind>([\"click\", \"type\", \"navigate\"]);\nconst allowedactions = new Set<actionkind>([\"observe\", \"inspect\", \"focus\", \"click\", \"type\", \"navigate\"]);\n\n/** Normalizes a user supplied HTTPS endpoint without preserving a provider lock-in. */\nexport function normalizeendpoint(value: string): endpointconfig {\n const endpoint = new URL(value.trim());\n if (endpoint.protocol !== \"https:\") throw new Error(\"Devthink accepts HTTPS endpoints only.\");\n if (endpoint.username || endpoint.password) throw new Error(\"Endpoint credentials are not allowed in the URL.\");\n return { endpoint: endpoint.toString(), origin: endpoint.origin, configuredat: Date.now() };\n}\n\n/** Creates the exact optional host pattern requested from Chromium. */\nexport function hostpattern(origin: string): string {\n const parsed = new URL(origin);\n if (parsed.protocol !== \"https:\") throw new Error(\"Only HTTPS origins can be granted.\");\n return `${parsed.origin}/*`;\n}\n\n/** Defines action risk from a fixed local allowlist. */\nexport function actionrisk(kind: actionkind): \"read\" | \"interaction\" | \"sensitive\" {\n if (!allowedactions.has(kind)) throw new Error(\"Unsupported browser action.\");\n if (sensitiveactions.has(kind)) return \"sensitive\";\n return kind === \"focus\" ? \"interaction\" : \"read\";\n}\n\n/** Validates a single proposal against the active tab origin and local policy. */\nexport function validatestep(step: toolstep, origin: string): policyevaluation {\n if (!allowedactions.has(step.kind)) return { allowed: false, reason: \"Unsupported action kind.\" };\n if (!step.summary.trim()) return { allowed: false, reason: \"A human-readable action summary is required.\" };\n if ((step.kind === \"click\" || step.kind === \"focus\" || step.kind === \"inspect\" || step.kind === \"type\") && !step.target?.trim()) return { allowed: false, reason: \"A page target is required.\" };\n if (step.kind === \"navigate\") {\n if (!step.value) return { allowed: false, reason: \"A navigation URL is required.\" };\n try {\n if (new URL(step.value).origin !== origin) return { allowed: false, reason: \"Navigation must remain within the approved origin.\" };\n } catch {\n return { allowed: false, reason: \"Navigation URL is invalid.\" };\n }\n }\n return { allowed: true };\n}\n\n/** Applies the consent gate immediately before an action reaches the page bridge. */\nexport function canexecute(input: { session: agentsession | undefined; plan: agentplan | undefined; step: toolstep; tabid: number; origin: string; now?: number }): policyevaluation {\n const now = input.now ?? Date.now();\n if (!input.session || input.session.stoppedat) return { allowed: false, reason: \"No active browser session exists.\" };\n if (input.session.expiresat <= now) return { allowed: false, reason: \"The browser session has expired.\" };\n if (input.session.tabid !== input.tabid || input.session.origin !== input.origin) return { allowed: false, reason: \"The action is outside the approved tab or origin.\" };\n if (!input.plan || input.plan.state !== \"approved\") return { allowed: false, reason: \"The plan has not received explicit approval.\" };\n if (input.plan.expiresat <= now) return { allowed: false, reason: \"The approved plan has expired.\" };\n return validatestep(input.step, input.origin);\n}\n\n/** Allows a non-mutating, temporary target preview during plan review. */\nexport function canpreview(input: { session: agentsession | undefined; plan: agentplan | undefined; step: toolstep; tabid: number; origin: string; now?: number }): policyevaluation {\n const now = input.now ?? Date.now();\n if (!input.session || input.session.stoppedat) return { allowed: false, reason: \"No active browser session exists.\" };\n if (input.session.expiresat <= now) return { allowed: false, reason: \"The browser session has expired.\" };\n if (input.session.tabid !== input.tabid || input.session.origin !== input.origin) return { allowed: false, reason: \"The preview is outside the approved tab or origin.\" };\n if (!input.plan || ![\"pending\", \"approved\"].includes(input.plan.state)) return { allowed: false, reason: \"Only a reviewed pending or approved plan can be previewed.\" };\n if (input.plan.expiresat <= now) return { allowed: false, reason: \"The reviewed plan has expired.\" };\n if (![\"focus\", \"inspect\", \"click\", \"type\"].includes(input.step.kind)) return { allowed: false, reason: \"Only a target-based action can be previewed.\" };\n return validatestep(input.step, input.origin);\n}\n", "/** Canonical package version synchronized from package.json. */\nexport const packageversion = \"1.1.30\" as const;\n", "/** Shared contracts for every Devthink target. */\nimport { packageversion } from \"./version.js\";\n\nexport const protocolversion = packageversion;\n\nexport type actionkind = \"observe\" | \"inspect\" | \"focus\" | \"click\" | \"type\" | \"navigate\";\nexport type actionrisk = \"read\" | \"interaction\" | \"sensitive\";\nexport type planstate = \"draft\" | \"pending\" | \"approved\" | \"rejected\" | \"expired\" | \"completed\" | \"cancelled\";\nexport type auditkind = \"configure\" | \"session\" | \"observe\" | \"proposal\" | \"approval\" | \"action\" | \"error\" | \"stop\";\n\nexport interface toolstep {\n id: string;\n kind: actionkind;\n target?: string;\n value?: string;\n summary: string;\n risk: actionrisk;\n}\n\nexport interface agentplan {\n id: string;\n objective: string;\n origin: string;\n steps: toolstep[];\n createdat: number;\n expiresat: number;\n state: planstate;\n approvedat?: number;\n}\n\nexport interface agentsession {\n id: string;\n tabid: number;\n origin: string;\n startedat: number;\n expiresat: number;\n stoppedat?: number;\n}\n\nexport interface endpointconfig {\n endpoint: string;\n origin: string;\n configuredat: number;\n}\n\nexport interface observation {\n url: string;\n title: string;\n textpreview: string;\n textlength: number;\n forms: Array<{ label: string; type: string; name: string }>;\n interactive: Array<{ selector: string; role: string; label: string }>;\n capturedat: number;\n}\n\nexport interface auditevent {\n id: string;\n kind: auditkind;\n at: number;\n summary: string;\n sessionid?: string;\n planid?: string;\n stepid?: string;\n}\n\nexport interface diagnosticreport {\n id: string;\n sessionid: string;\n origin: string;\n capturedat: number;\n tabid: number;\n title: string;\n textlength: number;\n interactivecount: number;\n formcount: number;\n bridgeavailable: boolean;\n}\n\nexport interface proposalrequest {\n objective: string;\n session: agentsession;\n observation: observation;\n}\n\nexport interface planproposal {\n version: typeof protocolversion;\n plan: agentplan;\n}\n\nexport interface policyevaluation {\n allowed: boolean;\n reason?: string;\n}\n", "import { actionrisk, validatestep } from \"./policy.js\";\nimport { protocolversion, type agentplan, type planproposal, type proposalrequest, type toolstep } from \"./types.js\";\n\nfunction record(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) throw new Error(\"Protocol message must be an object.\");\n return value as Record<string, unknown>;\n}\n\nfunction text(value: unknown, field: string): string {\n if (typeof value !== \"string\" || !value.trim()) throw new Error(`${field} must be a non-empty string.`);\n return value.trim();\n}\n\n/** Validates agent output before it becomes a locally reviewable plan. */\nexport function parseproposal(value: unknown, origin: string): planproposal {\n const root = record(value);\n if (root.version !== protocolversion) throw new Error(\"Unsupported protocol version.\");\n const planinput = record(root.plan);\n const stepsinput = planinput.steps;\n if (!Array.isArray(stepsinput) || stepsinput.length === 0 || stepsinput.length > 20) throw new Error(\"A plan needs between one and twenty steps.\");\n const steps: toolstep[] = stepsinput.map((input, index) => {\n const candidate = record(input);\n const kind = text(candidate.kind, `step ${index + 1} kind`) as toolstep[\"kind\"];\n const step: toolstep = {\n id: typeof candidate.id === \"string\" ? candidate.id : crypto.randomUUID(),\n kind,\n summary: text(candidate.summary, `step ${index + 1} summary`),\n risk: actionrisk(kind),\n ...(typeof candidate.target === \"string\" ? { target: candidate.target } : {}),\n ...(typeof candidate.value === \"string\" ? { value: candidate.value } : {}),\n };\n const evaluation = validatestep(step, origin);\n if (!evaluation.allowed) throw new Error(evaluation.reason);\n return step;\n });\n const createdat = Date.now();\n const plan: agentplan = {\n id: typeof planinput.id === \"string\" ? planinput.id : crypto.randomUUID(),\n objective: text(planinput.objective, \"objective\"),\n origin,\n steps,\n createdat,\n expiresat: Math.min(typeof planinput.expiresat === \"number\" ? planinput.expiresat : createdat + 10 * 60 * 1000, createdat + 30 * 60 * 1000),\n state: \"pending\",\n };\n if (plan.expiresat <= createdat) throw new Error(\"Plan expiry must be in the future.\");\n return { version: protocolversion, plan };\n}\n\n/** Shapes the only data that may be sent to a user-configured agent endpoint. */\nexport function requestbody(input: proposalrequest): string {\n return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation });\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { agentplan, agentsession, auditevent, diagnosticreport, endpointconfig, planprogress } from \"./types.js\";\n\n/** Provides a small storage seam that works in browser, tests and future adapters. */\nexport interface memoryadapter {\n get<T>(key: string): Promise<T | undefined>;\n set<T>(key: string, value: T): Promise<void>;\n}\n\nexport class sessionmemory {\n constructor(private readonly adapter: memoryadapter) {}\n\n async getconfig(): Promise<endpointconfig | undefined> { return this.adapter.get<endpointconfig>(\"config\"); }\n async setconfig(value: endpointconfig): Promise<void> { return this.adapter.set(\"config\", value); }\n async getsession(): Promise<agentsession | undefined> { return this.adapter.get<agentsession>(\"session\"); }\n async setsession(value: agentsession): Promise<void> { return this.adapter.set(\"session\", value); }\n async getplan(): Promise<agentplan | undefined> { return this.adapter.get<agentplan>(\"plan\"); }\n async setplan(value: agentplan): Promise<void> { return this.adapter.set(\"plan\", value); }\n async getdiagnostic(): Promise<diagnosticreport | undefined> { return this.adapter.get<diagnosticreport>(\"diagnostic\"); }\n async setdiagnostic(value: diagnosticreport): Promise<void> { return this.adapter.set(\"diagnostic\", value); }\n async getprogress(): Promise<planprogress | undefined> { return this.adapter.get<planprogress>(\"progress\"); }\n async setprogress(value: planprogress): Promise<void> { return this.adapter.set(\"progress\", value); }\n async getaudit(): Promise<auditevent[]> { return (await this.adapter.get<auditevent[]>(\"audit\")) ?? []; }\n async addaudi(event: auditevent): Promise<void> {\n const records = await this.getaudit();\n await this.adapter.set(\"audit\", [event, ...records].slice(0, 100));\n }\n}\n\n/** Creates identifiers locally without a network dependency. */\nexport function randomid(): string {\n return crypto.randomUUID();\n}\n", "import type { actionkind, agentplan, agentsession, endpointconfig, policyevaluation, toolstep } from \"./types.js\";\n\nconst sensitiveactions = new Set<actionkind>([\"click\", \"type\", \"navigate\", \"select\"]);\nconst interactionactions = new Set<actionkind>([\"focus\", \"scroll\", \"hover\"]);\nconst allowedactions = new Set<actionkind>([\"observe\", \"inspect\", \"focus\", \"click\", \"type\", \"navigate\", \"scroll\", \"select\", \"hover\", \"extract\", \"wait\"]);\nconst targetactions = new Set<actionkind>([\"inspect\", \"focus\", \"click\", \"type\", \"scroll\", \"select\", \"hover\"]);\n\n/** Normalizes a user supplied HTTPS endpoint without preserving a provider lock-in. */\nexport function normalizeendpoint(value: string): endpointconfig {\n const endpoint = new URL(value.trim());\n if (endpoint.protocol !== \"https:\") throw new Error(\"Devthink accepts HTTPS endpoints only.\");\n if (endpoint.username || endpoint.password) throw new Error(\"Endpoint credentials are not allowed in the URL.\");\n return { endpoint: endpoint.toString(), origin: endpoint.origin, configuredat: Date.now() };\n}\n\n/** Creates the exact optional host pattern requested from Chromium. */\nexport function hostpattern(origin: string): string {\n const parsed = new URL(origin);\n if (parsed.protocol !== \"https:\") throw new Error(\"Only HTTPS origins can be granted.\");\n return `${parsed.origin}/*`;\n}\n\n/** Defines action risk from a fixed local allowlist. */\nexport function actionrisk(kind: actionkind): \"read\" | \"interaction\" | \"sensitive\" {\n if (!allowedactions.has(kind)) throw new Error(\"Unsupported browser action.\");\n if (sensitiveactions.has(kind)) return \"sensitive\";\n return interactionactions.has(kind) ? \"interaction\" : \"read\";\n}\n\n/** Parses the bounded pause duration of a wait step. */\nexport function waitduration(step: toolstep): number {\n const requested = step.value ? Number.parseInt(step.value, 10) : 250;\n if (!Number.isFinite(requested) || requested < 0) throw new Error(\"Wait duration must be zero or a positive number of milliseconds.\");\n return Math.min(requested, 10_000);\n}\n\n/** Validates a single proposal against the active tab origin and local policy. */\nexport function validatestep(step: toolstep, origin: string): policyevaluation {\n if (!allowedactions.has(step.kind)) return { allowed: false, reason: \"Unsupported action kind.\" };\n if (!step.summary.trim()) return { allowed: false, reason: \"A human-readable action summary is required.\" };\n if (targetactions.has(step.kind) && !step.target?.trim()) return { allowed: false, reason: \"A page target is required.\" };\n if (step.kind === \"select\" && !step.value?.trim()) return { allowed: false, reason: \"A reviewed option value is required.\" };\n if (step.kind === \"wait\") {\n try { waitduration(step); } catch { return { allowed: false, reason: \"Wait duration must be zero or a positive number of milliseconds.\" }; }\n }\n if (step.kind === \"navigate\") {\n if (!step.value) return { allowed: false, reason: \"A navigation URL is required.\" };\n try {\n if (new URL(step.value).origin !== origin) return { allowed: false, reason: \"Navigation must remain within the approved origin.\" };\n } catch {\n return { allowed: false, reason: \"Navigation URL is invalid.\" };\n }\n }\n return { allowed: true };\n}\n\n/** Shared session gate: a live, unpaused session that still matches the active tab. */\nfunction sessiongate(input: { session: agentsession | undefined; tabid: number; origin: string; now: number; action: string }): policyevaluation {\n if (!input.session || input.session.stoppedat) return { allowed: false, reason: \"No active browser session exists.\" };\n if (input.session.expiresat <= input.now) return { allowed: false, reason: \"The browser session has expired.\" };\n if (input.session.pausedat) return { allowed: false, reason: `The browser session is paused and cannot ${input.action}.` };\n if (input.session.tabid !== input.tabid || input.session.origin !== input.origin) return { allowed: false, reason: `The ${input.action} is outside the approved tab or origin.` };\n return { allowed: true };\n}\n\n/** Applies the consent gate immediately before an action reaches the page bridge. */\nexport function canexecute(input: { session: agentsession | undefined; plan: agentplan | undefined; step: toolstep; tabid: number; origin: string; now?: number }): policyevaluation {\n const now = input.now ?? Date.now();\n const gate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: \"execute an action\" });\n if (!gate.allowed) return gate;\n if (!input.plan || input.plan.state !== \"approved\") return { allowed: false, reason: \"The plan has not received explicit approval.\" };\n if (input.plan.expiresat <= now) return { allowed: false, reason: \"The approved plan has expired.\" };\n return validatestep(input.step, input.origin);\n}\n\n/** Allows a non-mutating, temporary target preview during plan review. */\nexport function canpreview(input: { session: agentsession | undefined; plan: agentplan | undefined; step: toolstep; tabid: number; origin: string; now?: number }): policyevaluation {\n const now = input.now ?? Date.now();\n const gate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: \"preview a target\" });\n if (!gate.allowed) return gate;\n if (!input.plan || ![\"pending\", \"approved\"].includes(input.plan.state)) return { allowed: false, reason: \"Only a reviewed pending or approved plan can be previewed.\" };\n if (input.plan.expiresat <= now) return { allowed: false, reason: \"The reviewed plan has expired.\" };\n if (![\"focus\", \"inspect\", \"click\", \"type\", \"scroll\", \"select\", \"hover\"].includes(input.step.kind)) return { allowed: false, reason: \"Only a target-based action can be previewed.\" };\n return validatestep(input.step, input.origin);\n}\n", "/** Canonical package version synchronized from package.json. */\nexport const packageversion = \"1.1.31\" as const;\n", "/** Shared contracts for every Devthink target. */\nimport { packageversion } from \"./version.js\";\n\nexport const protocolversion = packageversion;\n\nexport type actionkind = \"observe\" | \"inspect\" | \"focus\" | \"click\" | \"type\" | \"navigate\" | \"scroll\" | \"select\" | \"hover\" | \"extract\" | \"wait\";\nexport type actionrisk = \"read\" | \"interaction\" | \"sensitive\";\nexport type planstate = \"draft\" | \"pending\" | \"approved\" | \"rejected\" | \"expired\" | \"completed\" | \"cancelled\";\nexport type auditkind = \"configure\" | \"session\" | \"observe\" | \"proposal\" | \"approval\" | \"action\" | \"error\" | \"stop\" | \"pause\" | \"resume\" | \"complete\";\n\nexport interface toolstep {\n id: string;\n kind: actionkind;\n target?: string;\n value?: string;\n summary: string;\n risk: actionrisk;\n}\n\nexport interface agentplan {\n id: string;\n objective: string;\n origin: string;\n steps: toolstep[];\n createdat: number;\n expiresat: number;\n state: planstate;\n approvedat?: number;\n completedat?: number;\n}\n\nexport interface agentsession {\n id: string;\n tabid: number;\n origin: string;\n startedat: number;\n expiresat: number;\n stoppedat?: number;\n pausedat?: number;\n}\n\nexport interface endpointconfig {\n endpoint: string;\n origin: string;\n configuredat: number;\n}\n\nexport interface observation {\n url: string;\n title: string;\n textpreview: string;\n textlength: number;\n forms: Array<{ label: string; type: string; name: string; options?: string[] }>;\n interactive: Array<{ selector: string; role: string; label: string }>;\n capturedat: number;\n}\n\nexport interface auditevent {\n id: string;\n kind: auditkind;\n at: number;\n summary: string;\n sessionid?: string;\n planid?: string;\n stepid?: string;\n}\n\nexport interface diagnosticreport {\n id: string;\n sessionid: string;\n origin: string;\n capturedat: number;\n tabid: number;\n title: string;\n textlength: number;\n interactivecount: number;\n formcount: number;\n bridgeavailable: boolean;\n}\n\nexport interface proposalrequest {\n objective: string;\n session: agentsession;\n observation: observation;\n}\n\nexport interface planproposal {\n version: typeof protocolversion;\n plan: agentplan;\n}\n\nexport interface policyevaluation {\n allowed: boolean;\n reason?: string;\n}\n\n/** Tracks which reviewed steps of one plan have already executed locally. */\nexport interface planprogress {\n planid: string;\n completedsteps: string[];\n updatedat: number;\n}\n", "import { actionrisk, validatestep } from \"./policy.js\";\nimport { protocolversion, type agentplan, type planproposal, type proposalrequest, type toolstep } from \"./types.js\";\n\nfunction record(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) throw new Error(\"Protocol message must be an object.\");\n return value as Record<string, unknown>;\n}\n\nfunction text(value: unknown, field: string): string {\n if (typeof value !== \"string\" || !value.trim()) throw new Error(`${field} must be a non-empty string.`);\n return value.trim();\n}\n\n/** Validates agent output before it becomes a locally reviewable plan. */\nexport function parseproposal(value: unknown, origin: string): planproposal {\n const root = record(value);\n if (root.version !== protocolversion) throw new Error(\"Unsupported protocol version.\");\n const planinput = record(root.plan);\n const stepsinput = planinput.steps;\n if (!Array.isArray(stepsinput) || stepsinput.length === 0 || stepsinput.length > 50) throw new Error(\"A plan needs between one and fifty steps.\");\n const steps: toolstep[] = stepsinput.map((input, index) => {\n const candidate = record(input);\n const kind = text(candidate.kind, `step ${index + 1} kind`) as toolstep[\"kind\"];\n const step: toolstep = {\n id: typeof candidate.id === \"string\" ? candidate.id : crypto.randomUUID(),\n kind,\n summary: text(candidate.summary, `step ${index + 1} summary`),\n risk: actionrisk(kind),\n ...(typeof candidate.target === \"string\" ? { target: candidate.target } : {}),\n ...(typeof candidate.value === \"string\" ? { value: candidate.value } : {}),\n };\n const evaluation = validatestep(step, origin);\n if (!evaluation.allowed) throw new Error(evaluation.reason);\n return step;\n });\n const createdat = Date.now();\n const plan: agentplan = {\n id: typeof planinput.id === \"string\" ? planinput.id : crypto.randomUUID(),\n objective: text(planinput.objective, \"objective\"),\n origin,\n steps,\n createdat,\n expiresat: Math.min(typeof planinput.expiresat === \"number\" ? planinput.expiresat : createdat + 10 * 60 * 1000, createdat + 30 * 60 * 1000),\n state: \"pending\",\n };\n if (plan.expiresat <= createdat) throw new Error(\"Plan expiry must be in the future.\");\n return { version: protocolversion, plan };\n}\n\n/** Shapes the only data that may be sent to a user-configured agent endpoint. */\nexport function requestbody(input: proposalrequest): string {\n return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation });\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/memory.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { agentplan, agentsession, auditevent, diagnosticreport, endpointconfig } from "./types.js";
1
+ import type { agentplan, agentsession, auditevent, diagnosticreport, endpointconfig, planprogress } from "./types.js";
2
2
  /** Provides a small storage seam that works in browser, tests and future adapters. */
3
3
  export interface memoryadapter {
4
4
  get<T>(key: string): Promise<T | undefined>;
@@ -15,6 +15,8 @@ export declare class sessionmemory {
15
15
  setplan(value: agentplan): Promise<void>;
16
16
  getdiagnostic(): Promise<diagnosticreport | undefined>;
17
17
  setdiagnostic(value: diagnosticreport): Promise<void>;
18
+ getprogress(): Promise<planprogress | undefined>;
19
+ setprogress(value: planprogress): Promise<void>;
18
20
  getaudit(): Promise<auditevent[]>;
19
21
  addaudi(event: auditevent): Promise<void>;
20
22
  }
@@ -1 +1 @@
1
- {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAExG,sFAAsF;AACtF,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC5C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;AAED,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,OAAO;IAApC,YAA6B,OAAO,EAAE,aAAa,EAAI;IAEjD,SAAS,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAuD;IACvG,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAA8C;IAC7F,UAAU,IAAI,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAAsD;IACrG,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAA+C;IAC7F,OAAO,IAAI,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAAgD;IACzF,OAAO,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAA4C;IACpF,aAAa,IAAI,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAA6D;IACnH,aAAa,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAkD;IACvG,QAAQ,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAAkE;IACnG,OAAO,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9C;CACF;AAED,gEAAgE;AAChE,wBAAgB,QAAQ,IAAI,MAAM,CAEjC"}
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtH,sFAAsF;AACtF,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC5C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;AAED,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,OAAO;IAApC,YAA6B,OAAO,EAAE,aAAa,EAAI;IAEjD,SAAS,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAuD;IACvG,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAA8C;IAC7F,UAAU,IAAI,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAAsD;IACrG,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAA+C;IAC7F,OAAO,IAAI,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAAgD;IACzF,OAAO,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAA4C;IACpF,aAAa,IAAI,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAA6D;IACnH,aAAa,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAkD;IACvG,WAAW,IAAI,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAAuD;IACvG,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAgD;IAC/F,QAAQ,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAAkE;IACnG,OAAO,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9C;CACF;AAED,gEAAgE;AAChE,wBAAgB,QAAQ,IAAI,MAAM,CAEjC"}
package/dist/policy.d.ts CHANGED
@@ -5,6 +5,8 @@ export declare function normalizeendpoint(value: string): endpointconfig;
5
5
  export declare function hostpattern(origin: string): string;
6
6
  /** Defines action risk from a fixed local allowlist. */
7
7
  export declare function actionrisk(kind: actionkind): "read" | "interaction" | "sensitive";
8
+ /** Parses the bounded pause duration of a wait step. */
9
+ export declare function waitduration(step: toolstep): number;
8
10
  /** Validates a single proposal against the active tab origin and local policy. */
9
11
  export declare function validatestep(step: toolstep, origin: string): policyevaluation;
10
12
  /** Applies the consent gate immediately before an action reaches the page bridge. */
@@ -1 +1 @@
1
- {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAKlH,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAK/D;AAED,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,aAAa,GAAG,WAAW,CAIjF;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAa7E;AAED,qFAAqF;AACrF,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,YAAY,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,gBAAgB,CAQnL;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,YAAY,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,gBAAgB,CASnL"}
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAOlH,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAK/D;AAED,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,aAAa,GAAG,WAAW,CAIjF;AAED,wDAAwD;AACxD,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAInD;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAiB7E;AAWD,qFAAqF;AACrF,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,YAAY,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,gBAAgB,CAOnL;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,YAAY,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,gBAAgB,CAQnL"}
package/dist/types.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- export declare const protocolversion: "1.1.30";
2
- export type actionkind = "observe" | "inspect" | "focus" | "click" | "type" | "navigate";
1
+ export declare const protocolversion: "1.1.31";
2
+ export type actionkind = "observe" | "inspect" | "focus" | "click" | "type" | "navigate" | "scroll" | "select" | "hover" | "extract" | "wait";
3
3
  export type actionrisk = "read" | "interaction" | "sensitive";
4
4
  export type planstate = "draft" | "pending" | "approved" | "rejected" | "expired" | "completed" | "cancelled";
5
- export type auditkind = "configure" | "session" | "observe" | "proposal" | "approval" | "action" | "error" | "stop";
5
+ export type auditkind = "configure" | "session" | "observe" | "proposal" | "approval" | "action" | "error" | "stop" | "pause" | "resume" | "complete";
6
6
  export interface toolstep {
7
7
  id: string;
8
8
  kind: actionkind;
@@ -20,6 +20,7 @@ export interface agentplan {
20
20
  expiresat: number;
21
21
  state: planstate;
22
22
  approvedat?: number;
23
+ completedat?: number;
23
24
  }
24
25
  export interface agentsession {
25
26
  id: string;
@@ -28,6 +29,7 @@ export interface agentsession {
28
29
  startedat: number;
29
30
  expiresat: number;
30
31
  stoppedat?: number;
32
+ pausedat?: number;
31
33
  }
32
34
  export interface endpointconfig {
33
35
  endpoint: string;
@@ -43,6 +45,7 @@ export interface observation {
43
45
  label: string;
44
46
  type: string;
45
47
  name: string;
48
+ options?: string[];
46
49
  }>;
47
50
  interactive: Array<{
48
51
  selector: string;
@@ -85,4 +88,10 @@ export interface policyevaluation {
85
88
  allowed: boolean;
86
89
  reason?: string;
87
90
  }
91
+ /** Tracks which reviewed steps of one plan have already executed locally. */
92
+ export interface planprogress {
93
+ planid: string;
94
+ completedsteps: string[];
95
+ updatedat: number;
96
+ }
88
97
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Canonical package version synchronized from package.json. */
2
- export declare const packageversion: "1.1.30";
2
+ export declare const packageversion: "1.1.31";
3
3
  //# sourceMappingURL=version.d.ts.map
@@ -28,6 +28,12 @@ var sessionmemory = class {
28
28
  async setdiagnostic(value) {
29
29
  return this.adapter.set("diagnostic", value);
30
30
  }
31
+ async getprogress() {
32
+ return this.adapter.get("progress");
33
+ }
34
+ async setprogress(value) {
35
+ return this.adapter.set("progress", value);
36
+ }
31
37
  async getaudit() {
32
38
  return await this.adapter.get("audit") ?? [];
33
39
  }
@@ -41,8 +47,10 @@ function randomid() {
41
47
  }
42
48
 
43
49
  // policy.ts
44
- var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate"]);
45
- var allowedactions = /* @__PURE__ */ new Set(["observe", "inspect", "focus", "click", "type", "navigate"]);
50
+ var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select"]);
51
+ var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover"]);
52
+ var allowedactions = /* @__PURE__ */ new Set(["observe", "inspect", "focus", "click", "type", "navigate", "scroll", "select", "hover", "extract", "wait"]);
53
+ var targetactions = /* @__PURE__ */ new Set(["inspect", "focus", "click", "type", "scroll", "select", "hover"]);
46
54
  function normalizeendpoint(value) {
47
55
  const endpoint = new URL(value.trim());
48
56
  if (endpoint.protocol !== "https:") throw new Error("Devthink accepts HTTPS endpoints only.");
@@ -57,12 +65,25 @@ function hostpattern(origin) {
57
65
  function actionrisk(kind) {
58
66
  if (!allowedactions.has(kind)) throw new Error("Unsupported browser action.");
59
67
  if (sensitiveactions.has(kind)) return "sensitive";
60
- return kind === "focus" ? "interaction" : "read";
68
+ return interactionactions.has(kind) ? "interaction" : "read";
69
+ }
70
+ function waitduration(step) {
71
+ const requested = step.value ? Number.parseInt(step.value, 10) : 250;
72
+ if (!Number.isFinite(requested) || requested < 0) throw new Error("Wait duration must be zero or a positive number of milliseconds.");
73
+ return Math.min(requested, 1e4);
61
74
  }
62
75
  function validatestep(step, origin) {
63
76
  if (!allowedactions.has(step.kind)) return { allowed: false, reason: "Unsupported action kind." };
64
77
  if (!step.summary.trim()) return { allowed: false, reason: "A human-readable action summary is required." };
65
- if ((step.kind === "click" || step.kind === "focus" || step.kind === "inspect" || step.kind === "type") && !step.target?.trim()) return { allowed: false, reason: "A page target is required." };
78
+ if (targetactions.has(step.kind) && !step.target?.trim()) return { allowed: false, reason: "A page target is required." };
79
+ if (step.kind === "select" && !step.value?.trim()) return { allowed: false, reason: "A reviewed option value is required." };
80
+ if (step.kind === "wait") {
81
+ try {
82
+ waitduration(step);
83
+ } catch {
84
+ return { allowed: false, reason: "Wait duration must be zero or a positive number of milliseconds." };
85
+ }
86
+ }
66
87
  if (step.kind === "navigate") {
67
88
  if (!step.value) return { allowed: false, reason: "A navigation URL is required." };
68
89
  try {
@@ -73,28 +94,52 @@ function validatestep(step, origin) {
73
94
  }
74
95
  return { allowed: true };
75
96
  }
97
+ function sessiongate(input) {
98
+ if (!input.session || input.session.stoppedat) return { allowed: false, reason: "No active browser session exists." };
99
+ if (input.session.expiresat <= input.now) return { allowed: false, reason: "The browser session has expired." };
100
+ if (input.session.pausedat) return { allowed: false, reason: `The browser session is paused and cannot ${input.action}.` };
101
+ if (input.session.tabid !== input.tabid || input.session.origin !== input.origin) return { allowed: false, reason: `The ${input.action} is outside the approved tab or origin.` };
102
+ return { allowed: true };
103
+ }
76
104
  function canexecute(input) {
77
105
  const now = input.now ?? Date.now();
78
- if (!input.session || input.session.stoppedat) return { allowed: false, reason: "No active browser session exists." };
79
- if (input.session.expiresat <= now) return { allowed: false, reason: "The browser session has expired." };
80
- if (input.session.tabid !== input.tabid || input.session.origin !== input.origin) return { allowed: false, reason: "The action is outside the approved tab or origin." };
106
+ const gate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: "execute an action" });
107
+ if (!gate.allowed) return gate;
81
108
  if (!input.plan || input.plan.state !== "approved") return { allowed: false, reason: "The plan has not received explicit approval." };
82
109
  if (input.plan.expiresat <= now) return { allowed: false, reason: "The approved plan has expired." };
83
110
  return validatestep(input.step, input.origin);
84
111
  }
85
112
  function canpreview(input) {
86
113
  const now = input.now ?? Date.now();
87
- if (!input.session || input.session.stoppedat) return { allowed: false, reason: "No active browser session exists." };
88
- if (input.session.expiresat <= now) return { allowed: false, reason: "The browser session has expired." };
89
- if (input.session.tabid !== input.tabid || input.session.origin !== input.origin) return { allowed: false, reason: "The preview is outside the approved tab or origin." };
114
+ const gate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: "preview a target" });
115
+ if (!gate.allowed) return gate;
90
116
  if (!input.plan || !["pending", "approved"].includes(input.plan.state)) return { allowed: false, reason: "Only a reviewed pending or approved plan can be previewed." };
91
117
  if (input.plan.expiresat <= now) return { allowed: false, reason: "The reviewed plan has expired." };
92
- if (!["focus", "inspect", "click", "type"].includes(input.step.kind)) return { allowed: false, reason: "Only a target-based action can be previewed." };
118
+ if (!["focus", "inspect", "click", "type", "scroll", "select", "hover"].includes(input.step.kind)) return { allowed: false, reason: "Only a target-based action can be previewed." };
93
119
  return validatestep(input.step, input.origin);
94
120
  }
95
121
 
122
+ // progress.ts
123
+ function emptyprogress(planid, now) {
124
+ return { planid, completedsteps: [], updatedat: now };
125
+ }
126
+ function recordstep(progress, planid, stepid, now) {
127
+ const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
128
+ if (base.completedsteps.includes(stepid)) return { ...base, updatedat: now };
129
+ return { planid, completedsteps: [...base.completedsteps, stepid], updatedat: now };
130
+ }
131
+ function iscomplete(progress, plan) {
132
+ if (!progress || progress.planid !== plan.id) return false;
133
+ const required = plan.steps.map((step) => step.id);
134
+ return required.length > 0 && required.every((id) => progress.completedsteps.includes(id));
135
+ }
136
+ function resetforplan(progress, plan, now) {
137
+ if (progress && progress.planid === plan.id) return progress;
138
+ return emptyprogress(plan.id, now);
139
+ }
140
+
96
141
  // version.ts
97
- var packageversion = "1.1.30";
142
+ var packageversion = "1.1.31";
98
143
 
99
144
  // types.ts
100
145
  var protocolversion = packageversion;
@@ -113,7 +158,7 @@ function parseproposal(value, origin) {
113
158
  if (root.version !== protocolversion) throw new Error("Unsupported protocol version.");
114
159
  const planinput = record(root.plan);
115
160
  const stepsinput = planinput.steps;
116
- if (!Array.isArray(stepsinput) || stepsinput.length === 0 || stepsinput.length > 20) throw new Error("A plan needs between one and twenty steps.");
161
+ if (!Array.isArray(stepsinput) || stepsinput.length === 0 || stepsinput.length > 50) throw new Error("A plan needs between one and fifty steps.");
117
162
  const steps = stepsinput.map((input, index) => {
118
163
  const candidate = record(input);
119
164
  const kind = text(candidate.kind, `step ${index + 1} kind`);
@@ -218,6 +263,7 @@ async function propose(objective, remote) {
218
263
  plan = parseproposal(await response.json(), session.origin).plan;
219
264
  }
220
265
  await memory.setplan(plan);
266
+ await memory.setprogress(resetforplan(await memory.getprogress(), plan, Date.now()));
221
267
  await audit("proposal", `Plan proposed with ${plan.steps.length} reviewed step${plan.steps.length === 1 ? "" : "s"}.`, { sessionid: session.id, planid: plan.id });
222
268
  return plan;
223
269
  }
@@ -239,6 +285,15 @@ async function executestep(stepid) {
239
285
  const output = result[0]?.result;
240
286
  const summary = output?.summary ?? "The page action returned no result.";
241
287
  await audit(output?.ok ? "action" : "error", summary, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {}, stepid });
288
+ if (output?.ok && plan) {
289
+ const progress = recordstep(await memory.getprogress(), plan.id, stepid, Date.now());
290
+ await memory.setprogress(progress);
291
+ if (iscomplete(progress, plan) && plan.state === "approved") {
292
+ const completed = { ...plan, state: "completed", completedat: Date.now() };
293
+ await memory.setplan(completed);
294
+ await audit("complete", "Every reviewed step of the approved plan has executed.", { ...session ? { sessionid: session.id } : {}, planid: completed.id });
295
+ }
296
+ }
242
297
  return output ?? { ok: false, summary };
243
298
  }
244
299
  async function previewstep(stepid) {
@@ -261,6 +316,26 @@ async function previewstep(stepid) {
261
316
  await audit(output?.ok ? "observe" : "error", summary, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {}, stepid });
262
317
  return output ?? { ok: false, summary };
263
318
  }
319
+ async function pausesession() {
320
+ const session = await memory.getsession();
321
+ if (!session || session.stoppedat) throw new Error("No active browser session exists.");
322
+ if (session.expiresat <= Date.now()) throw new Error("The browser session has expired.");
323
+ if (session.pausedat) throw new Error("The browser session is already paused.");
324
+ const paused = { ...session, pausedat: Date.now() };
325
+ await memory.setsession(paused);
326
+ await audit("pause", "The user paused the browser session; no action or preview can run.", { sessionid: session.id });
327
+ return paused;
328
+ }
329
+ async function resumesession() {
330
+ const session = await memory.getsession();
331
+ if (!session || session.stoppedat) throw new Error("No active browser session exists.");
332
+ if (session.expiresat <= Date.now()) throw new Error("The browser session has expired and cannot be resumed.");
333
+ if (!session.pausedat) throw new Error("The browser session is not paused.");
334
+ const resumed = { id: session.id, tabid: session.tabid, origin: session.origin, startedat: session.startedat, expiresat: session.expiresat };
335
+ await memory.setsession(resumed);
336
+ await audit("resume", "The user resumed the browser session; reviewed actions can run again.", { sessionid: session.id });
337
+ return resumed;
338
+ }
264
339
  async function handlerequest(message, sender) {
265
340
  if (!extensionpage(sender)) throw new Error("Requests are accepted only from Devthink extension pages.");
266
341
  const input = message;
@@ -275,8 +350,11 @@ async function handlerequest(message, sender) {
275
350
  }
276
351
  case "startsession":
277
352
  return startsession();
278
- case "context":
279
- return { config: await memory.getconfig(), session: await memory.getsession(), plan: await memory.getplan(), diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit() };
353
+ case "context": {
354
+ const plan = await memory.getplan();
355
+ const progress = await memory.getprogress();
356
+ return { config: await memory.getconfig(), session: await memory.getsession(), plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit() };
357
+ }
280
358
  case "diagnostic":
281
359
  return diagnostic();
282
360
  case "proposelocal":
@@ -305,6 +383,10 @@ async function handlerequest(message, sender) {
305
383
  return previewstep(input.stepid ?? "");
306
384
  case "execute":
307
385
  return executestep(input.stepid ?? "");
386
+ case "pausesession":
387
+ return pausesession();
388
+ case "resumesession":
389
+ return resumesession();
308
390
  case "stop": {
309
391
  const session = await memory.getsession();
310
392
  if (session) await memory.setsession({ ...session, stoppedat: Date.now() });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../memory.ts", "../../policy.ts", "../../version.ts", "../../types.ts", "../../protocol.ts", "../background.ts"],
4
- "sourcesContent": ["import type { agentplan, agentsession, auditevent, diagnosticreport, endpointconfig } from \"./types.js\";\n\n/** Provides a small storage seam that works in browser, tests and future adapters. */\nexport interface memoryadapter {\n get<T>(key: string): Promise<T | undefined>;\n set<T>(key: string, value: T): Promise<void>;\n}\n\nexport class sessionmemory {\n constructor(private readonly adapter: memoryadapter) {}\n\n async getconfig(): Promise<endpointconfig | undefined> { return this.adapter.get<endpointconfig>(\"config\"); }\n async setconfig(value: endpointconfig): Promise<void> { return this.adapter.set(\"config\", value); }\n async getsession(): Promise<agentsession | undefined> { return this.adapter.get<agentsession>(\"session\"); }\n async setsession(value: agentsession): Promise<void> { return this.adapter.set(\"session\", value); }\n async getplan(): Promise<agentplan | undefined> { return this.adapter.get<agentplan>(\"plan\"); }\n async setplan(value: agentplan): Promise<void> { return this.adapter.set(\"plan\", value); }\n async getdiagnostic(): Promise<diagnosticreport | undefined> { return this.adapter.get<diagnosticreport>(\"diagnostic\"); }\n async setdiagnostic(value: diagnosticreport): Promise<void> { return this.adapter.set(\"diagnostic\", value); }\n async getaudit(): Promise<auditevent[]> { return (await this.adapter.get<auditevent[]>(\"audit\")) ?? []; }\n async addaudi(event: auditevent): Promise<void> {\n const records = await this.getaudit();\n await this.adapter.set(\"audit\", [event, ...records].slice(0, 100));\n }\n}\n\n/** Creates identifiers locally without a network dependency. */\nexport function randomid(): string {\n return crypto.randomUUID();\n}\n", "import type { actionkind, agentplan, agentsession, endpointconfig, policyevaluation, toolstep } from \"./types.js\";\n\nconst sensitiveactions = new Set<actionkind>([\"click\", \"type\", \"navigate\"]);\nconst allowedactions = new Set<actionkind>([\"observe\", \"inspect\", \"focus\", \"click\", \"type\", \"navigate\"]);\n\n/** Normalizes a user supplied HTTPS endpoint without preserving a provider lock-in. */\nexport function normalizeendpoint(value: string): endpointconfig {\n const endpoint = new URL(value.trim());\n if (endpoint.protocol !== \"https:\") throw new Error(\"Devthink accepts HTTPS endpoints only.\");\n if (endpoint.username || endpoint.password) throw new Error(\"Endpoint credentials are not allowed in the URL.\");\n return { endpoint: endpoint.toString(), origin: endpoint.origin, configuredat: Date.now() };\n}\n\n/** Creates the exact optional host pattern requested from Chromium. */\nexport function hostpattern(origin: string): string {\n const parsed = new URL(origin);\n if (parsed.protocol !== \"https:\") throw new Error(\"Only HTTPS origins can be granted.\");\n return `${parsed.origin}/*`;\n}\n\n/** Defines action risk from a fixed local allowlist. */\nexport function actionrisk(kind: actionkind): \"read\" | \"interaction\" | \"sensitive\" {\n if (!allowedactions.has(kind)) throw new Error(\"Unsupported browser action.\");\n if (sensitiveactions.has(kind)) return \"sensitive\";\n return kind === \"focus\" ? \"interaction\" : \"read\";\n}\n\n/** Validates a single proposal against the active tab origin and local policy. */\nexport function validatestep(step: toolstep, origin: string): policyevaluation {\n if (!allowedactions.has(step.kind)) return { allowed: false, reason: \"Unsupported action kind.\" };\n if (!step.summary.trim()) return { allowed: false, reason: \"A human-readable action summary is required.\" };\n if ((step.kind === \"click\" || step.kind === \"focus\" || step.kind === \"inspect\" || step.kind === \"type\") && !step.target?.trim()) return { allowed: false, reason: \"A page target is required.\" };\n if (step.kind === \"navigate\") {\n if (!step.value) return { allowed: false, reason: \"A navigation URL is required.\" };\n try {\n if (new URL(step.value).origin !== origin) return { allowed: false, reason: \"Navigation must remain within the approved origin.\" };\n } catch {\n return { allowed: false, reason: \"Navigation URL is invalid.\" };\n }\n }\n return { allowed: true };\n}\n\n/** Applies the consent gate immediately before an action reaches the page bridge. */\nexport function canexecute(input: { session: agentsession | undefined; plan: agentplan | undefined; step: toolstep; tabid: number; origin: string; now?: number }): policyevaluation {\n const now = input.now ?? Date.now();\n if (!input.session || input.session.stoppedat) return { allowed: false, reason: \"No active browser session exists.\" };\n if (input.session.expiresat <= now) return { allowed: false, reason: \"The browser session has expired.\" };\n if (input.session.tabid !== input.tabid || input.session.origin !== input.origin) return { allowed: false, reason: \"The action is outside the approved tab or origin.\" };\n if (!input.plan || input.plan.state !== \"approved\") return { allowed: false, reason: \"The plan has not received explicit approval.\" };\n if (input.plan.expiresat <= now) return { allowed: false, reason: \"The approved plan has expired.\" };\n return validatestep(input.step, input.origin);\n}\n\n/** Allows a non-mutating, temporary target preview during plan review. */\nexport function canpreview(input: { session: agentsession | undefined; plan: agentplan | undefined; step: toolstep; tabid: number; origin: string; now?: number }): policyevaluation {\n const now = input.now ?? Date.now();\n if (!input.session || input.session.stoppedat) return { allowed: false, reason: \"No active browser session exists.\" };\n if (input.session.expiresat <= now) return { allowed: false, reason: \"The browser session has expired.\" };\n if (input.session.tabid !== input.tabid || input.session.origin !== input.origin) return { allowed: false, reason: \"The preview is outside the approved tab or origin.\" };\n if (!input.plan || ![\"pending\", \"approved\"].includes(input.plan.state)) return { allowed: false, reason: \"Only a reviewed pending or approved plan can be previewed.\" };\n if (input.plan.expiresat <= now) return { allowed: false, reason: \"The reviewed plan has expired.\" };\n if (![\"focus\", \"inspect\", \"click\", \"type\"].includes(input.step.kind)) return { allowed: false, reason: \"Only a target-based action can be previewed.\" };\n return validatestep(input.step, input.origin);\n}\n", "/** Canonical package version synchronized from package.json. */\nexport const packageversion = \"1.1.30\" as const;\n", "/** Shared contracts for every Devthink target. */\nimport { packageversion } from \"./version.js\";\n\nexport const protocolversion = packageversion;\n\nexport type actionkind = \"observe\" | \"inspect\" | \"focus\" | \"click\" | \"type\" | \"navigate\";\nexport type actionrisk = \"read\" | \"interaction\" | \"sensitive\";\nexport type planstate = \"draft\" | \"pending\" | \"approved\" | \"rejected\" | \"expired\" | \"completed\" | \"cancelled\";\nexport type auditkind = \"configure\" | \"session\" | \"observe\" | \"proposal\" | \"approval\" | \"action\" | \"error\" | \"stop\";\n\nexport interface toolstep {\n id: string;\n kind: actionkind;\n target?: string;\n value?: string;\n summary: string;\n risk: actionrisk;\n}\n\nexport interface agentplan {\n id: string;\n objective: string;\n origin: string;\n steps: toolstep[];\n createdat: number;\n expiresat: number;\n state: planstate;\n approvedat?: number;\n}\n\nexport interface agentsession {\n id: string;\n tabid: number;\n origin: string;\n startedat: number;\n expiresat: number;\n stoppedat?: number;\n}\n\nexport interface endpointconfig {\n endpoint: string;\n origin: string;\n configuredat: number;\n}\n\nexport interface observation {\n url: string;\n title: string;\n textpreview: string;\n textlength: number;\n forms: Array<{ label: string; type: string; name: string }>;\n interactive: Array<{ selector: string; role: string; label: string }>;\n capturedat: number;\n}\n\nexport interface auditevent {\n id: string;\n kind: auditkind;\n at: number;\n summary: string;\n sessionid?: string;\n planid?: string;\n stepid?: string;\n}\n\nexport interface diagnosticreport {\n id: string;\n sessionid: string;\n origin: string;\n capturedat: number;\n tabid: number;\n title: string;\n textlength: number;\n interactivecount: number;\n formcount: number;\n bridgeavailable: boolean;\n}\n\nexport interface proposalrequest {\n objective: string;\n session: agentsession;\n observation: observation;\n}\n\nexport interface planproposal {\n version: typeof protocolversion;\n plan: agentplan;\n}\n\nexport interface policyevaluation {\n allowed: boolean;\n reason?: string;\n}\n", "import { actionrisk, validatestep } from \"./policy.js\";\nimport { protocolversion, type agentplan, type planproposal, type proposalrequest, type toolstep } from \"./types.js\";\n\nfunction record(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) throw new Error(\"Protocol message must be an object.\");\n return value as Record<string, unknown>;\n}\n\nfunction text(value: unknown, field: string): string {\n if (typeof value !== \"string\" || !value.trim()) throw new Error(`${field} must be a non-empty string.`);\n return value.trim();\n}\n\n/** Validates agent output before it becomes a locally reviewable plan. */\nexport function parseproposal(value: unknown, origin: string): planproposal {\n const root = record(value);\n if (root.version !== protocolversion) throw new Error(\"Unsupported protocol version.\");\n const planinput = record(root.plan);\n const stepsinput = planinput.steps;\n if (!Array.isArray(stepsinput) || stepsinput.length === 0 || stepsinput.length > 20) throw new Error(\"A plan needs between one and twenty steps.\");\n const steps: toolstep[] = stepsinput.map((input, index) => {\n const candidate = record(input);\n const kind = text(candidate.kind, `step ${index + 1} kind`) as toolstep[\"kind\"];\n const step: toolstep = {\n id: typeof candidate.id === \"string\" ? candidate.id : crypto.randomUUID(),\n kind,\n summary: text(candidate.summary, `step ${index + 1} summary`),\n risk: actionrisk(kind),\n ...(typeof candidate.target === \"string\" ? { target: candidate.target } : {}),\n ...(typeof candidate.value === \"string\" ? { value: candidate.value } : {}),\n };\n const evaluation = validatestep(step, origin);\n if (!evaluation.allowed) throw new Error(evaluation.reason);\n return step;\n });\n const createdat = Date.now();\n const plan: agentplan = {\n id: typeof planinput.id === \"string\" ? planinput.id : crypto.randomUUID(),\n objective: text(planinput.objective, \"objective\"),\n origin,\n steps,\n createdat,\n expiresat: Math.min(typeof planinput.expiresat === \"number\" ? planinput.expiresat : createdat + 10 * 60 * 1000, createdat + 30 * 60 * 1000),\n state: \"pending\",\n };\n if (plan.expiresat <= createdat) throw new Error(\"Plan expiry must be in the future.\");\n return { version: protocolversion, plan };\n}\n\n/** Shapes the only data that may be sent to a user-configured agent endpoint. */\nexport function requestbody(input: proposalrequest): string {\n return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation });\n}\n", "import { randomid, sessionmemory } from \"../memory.js\";\nimport { canexecute, canpreview, hostpattern, normalizeendpoint } from \"../policy.js\";\nimport { parseproposal, requestbody } from \"../protocol.js\";\nimport type { agentplan, agentsession, auditevent, diagnosticreport, observation, toolstep } from \"../types.js\";\n\nconst sessionduration = 15 * 60 * 1000;\n\nconst chromestorage = {\n async get<T>(key: string): Promise<T | undefined> { return (await chrome.storage.local.get(key))[key] as T | undefined; },\n async set<T>(key: string, value: T): Promise<void> { await chrome.storage.local.set({ [key]: value }); },\n};\nconst memory = new sessionmemory(chromestorage);\n\nfunction extensionpage(sender: chrome.runtime.MessageSender): boolean {\n return sender.id === chrome.runtime.id && Boolean(sender.url?.startsWith(chrome.runtime.getURL(\"\")));\n}\n\nasync function audit(kind: auditevent[\"kind\"], summary: string, extra: Partial<auditevent> = {}): Promise<void> {\n await memory.addaudi({ id: randomid(), kind, at: Date.now(), summary, ...extra });\n}\n\nasync function activecontext(): Promise<{ tab: chrome.tabs.Tab; origin: string }> {\n const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true });\n if (!tab?.id || !tab.url) throw new Error(\"No active web tab is available.\");\n const origin = new URL(tab.url).origin;\n if (!origin.startsWith(\"https://\")) throw new Error(\"Devthink can work with HTTPS pages only.\");\n return { tab, origin };\n}\n\nasync function snapshot(tabid: number): Promise<observation> {\n await chrome.scripting.executeScript({ target: { tabId: tabid }, files: [\"pagebridge.js\"] });\n const result = await chrome.scripting.executeScript({ target: { tabId: tabid }, func: () => {\n const bridge = (globalThis as typeof globalThis & { devthinkbridge?: { capturesnapshot: () => observation } }).devthinkbridge;\n if (!bridge) throw new Error(\"Devthink page bridge is unavailable.\");\n return bridge.capturesnapshot();\n } });\n const value = result[0]?.result;\n if (!value) throw new Error(\"The page did not return an observation.\");\n return value as observation;\n}\n\nasync function startsession(): Promise<agentsession> {\n const { tab, origin } = await activecontext();\n const session: agentsession = { id: randomid(), tabid: tab.id as number, origin, startedat: Date.now(), expiresat: Date.now() + sessionduration };\n await memory.setsession(session);\n await audit(\"session\", `Session started for ${origin}.`, { sessionid: session.id });\n return session;\n}\n\nasync function diagnostic(): Promise<diagnosticreport> {\n const session = await memory.getsession();\n const { tab, origin } = await activecontext();\n if (!session || session.stoppedat || session.expiresat <= Date.now() || session.tabid !== tab.id || session.origin !== origin) throw new Error(\"Start a current session for this active tab before diagnostics.\");\n const observation = await snapshot(session.tabid);\n const report: diagnosticreport = { id: randomid(), sessionid: session.id, origin, capturedat: Date.now(), tabid: session.tabid, title: observation.title, textlength: observation.textlength, interactivecount: observation.interactive.length, formcount: observation.forms.length, bridgeavailable: true };\n await memory.setdiagnostic(report);\n await audit(\"observe\", \"Structured diagnostics captured for the approved active tab.\", { sessionid: session.id });\n return report;\n}\n\nfunction localplan(objective: string, session: agentsession): agentplan {\n const now = Date.now();\n return { id: randomid(), objective, origin: session.origin, steps: [{ id: randomid(), kind: \"observe\", summary: \"Capture a bounded semantic snapshot of the approved active tab.\", risk: \"read\" }], createdat: now, expiresat: now + sessionduration, state: \"pending\" };\n}\n\nasync function propose(objective: string, remote: boolean): Promise<agentplan> {\n if (!objective.trim()) throw new Error(\"An objective is required.\");\n const session = await memory.getsession();\n if (!session || session.stoppedat || session.expiresat <= Date.now()) throw new Error(\"Start a current browser session before requesting a plan.\");\n const { tab, origin } = await activecontext();\n if (session.tabid !== tab.id || session.origin !== origin) throw new Error(\"The selected tab or origin no longer matches the approved session.\");\n const observation = await snapshot(session.tabid);\n const config = await memory.getconfig();\n let plan = localplan(objective.trim(), session);\n if (remote) {\n if (!config) throw new Error(\"Configure an approved HTTPS endpoint before requesting a remote proposal.\");\n const response = await fetch(config.endpoint, { method: \"POST\", headers: { \"content-type\": \"application/json\" }, credentials: \"omit\", body: requestbody({ objective: objective.trim(), session, observation }) });\n if (!response.ok) throw new Error(`Proposal endpoint returned ${response.status}.`);\n plan = parseproposal(await response.json(), session.origin).plan;\n }\n await memory.setplan(plan);\n await audit(\"proposal\", `Plan proposed with ${plan.steps.length} reviewed step${plan.steps.length === 1 ? \"\" : \"s\"}.`, { sessionid: session.id, planid: plan.id });\n return plan;\n}\n\nasync function executestep(stepid: string): Promise<{ ok: boolean; summary: string }> {\n const session = await memory.getsession();\n const plan = await memory.getplan();\n const { tab, origin } = await activecontext();\n const step = plan?.steps.find(candidate => candidate.id === stepid);\n if (!step) throw new Error(\"Reviewed step was not found.\");\n const gate = canexecute({ session, plan, step, tabid: tab.id as number, origin });\n if (!gate.allowed) throw new Error(gate.reason);\n const fresh = await snapshot(tab.id as number);\n if (step.target && !fresh.interactive.some(item => item.selector === step.target)) throw new Error(\"The page changed and the target must be reviewed again.\");\n const result = await chrome.scripting.executeScript({ target: { tabId: tab.id as number }, func: (action: toolstep, expectedorigin: string) => {\n const bridge = (globalThis as typeof globalThis & { devthinkbridge?: { performstep: (input: toolstep, origin: string) => { ok: boolean; summary: string } } }).devthinkbridge;\n if (!bridge) throw new Error(\"Devthink page bridge is unavailable.\");\n return bridge.performstep(action, expectedorigin);\n }, args: [step, origin] });\n const output = result[0]?.result as { ok: boolean; summary: string } | undefined;\n const summary = output?.summary ?? \"The page action returned no result.\";\n await audit(output?.ok ? \"action\" : \"error\", summary, { ...(session ? { sessionid: session.id } : {}), ...(plan ? { planid: plan.id } : {}), stepid });\n return output ?? { ok: false, summary };\n}\n\n/** Highlights a reviewed target without clicking, typing, navigating or transmitting page data. */\nasync function previewstep(stepid: string): Promise<{ ok: boolean; summary: string }> {\n const session = await memory.getsession();\n const plan = await memory.getplan();\n const { tab, origin } = await activecontext();\n const step = plan?.steps.find(candidate => candidate.id === stepid);\n if (!step) throw new Error(\"Reviewed step was not found.\");\n const gate = canpreview({ session, plan, step, tabid: tab.id as number, origin });\n if (!gate.allowed) throw new Error(gate.reason);\n const fresh = await snapshot(tab.id as number);\n if (!step.target || !fresh.interactive.some(item => item.selector === step.target)) throw new Error(\"The page changed and the target must be reviewed again.\");\n const result = await chrome.scripting.executeScript({ target: { tabId: tab.id as number }, func: (target: string, expectedorigin: string) => {\n const bridge = (globalThis as typeof globalThis & { devthinkbridge?: { previewtarget: (selector: string, origin: string) => { ok: boolean; summary: string } } }).devthinkbridge;\n if (!bridge) throw new Error(\"Devthink page bridge is unavailable.\");\n return bridge.previewtarget(target, expectedorigin);\n }, args: [step.target, origin] });\n const output = result[0]?.result as { ok: boolean; summary: string } | undefined;\n const summary = output?.summary ?? \"The target preview returned no result.\";\n await audit(output?.ok ? \"observe\" : \"error\", summary, { ...(session ? { sessionid: session.id } : {}), ...(plan ? { planid: plan.id } : {}), stepid });\n return output ?? { ok: false, summary };\n}\n\nasync function handlerequest(message: unknown, sender: chrome.runtime.MessageSender): Promise<unknown> {\n if (!extensionpage(sender)) throw new Error(\"Requests are accepted only from Devthink extension pages.\");\n const input = message as { kind?: string; endpoint?: string; objective?: string; stepid?: string };\n switch (input.kind) {\n case \"configure\": {\n const config = normalizeendpoint(input.endpoint ?? \"\");\n const granted = await chrome.permissions.contains({ origins: [hostpattern(config.origin)] });\n if (!granted) throw new Error(\"The endpoint origin has not received optional permission.\");\n await memory.setconfig(config);\n await audit(\"configure\", `Configured user-selected endpoint ${config.origin}.`);\n return config;\n }\n case \"startsession\": return startsession();\n case \"context\": return { config: await memory.getconfig(), session: await memory.getsession(), plan: await memory.getplan(), diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit() };\n case \"diagnostic\": return diagnostic();\n case \"proposelocal\": return propose(input.objective ?? \"\", false);\n case \"proposeremote\": return propose(input.objective ?? \"\", true);\n case \"approve\": {\n const plan = await memory.getplan();\n if (!plan || plan.state !== \"pending\") throw new Error(\"Only a pending plan can be approved.\");\n const approved = { ...plan, state: \"approved\" as const, approvedat: Date.now() };\n await memory.setplan(approved);\n const current = await memory.getsession();\n await audit(\"approval\", \"The user approved the reviewed plan.\", { ...(current ? { sessionid: current.id } : {}), planid: approved.id });\n return approved;\n }\n case \"reject\": {\n const plan = await memory.getplan();\n if (!plan) throw new Error(\"No plan is available to reject.\");\n const rejected = { ...plan, state: \"rejected\" as const };\n await memory.setplan(rejected);\n const current = await memory.getsession();\n await audit(\"approval\", \"The user rejected the plan.\", { ...(current ? { sessionid: current.id } : {}), planid: rejected.id });\n return rejected;\n }\n case \"preview\": return previewstep(input.stepid ?? \"\");\n case \"execute\": return executestep(input.stepid ?? \"\");\n case \"stop\": {\n const session = await memory.getsession();\n if (session) await memory.setsession({ ...session, stoppedat: Date.now() });\n const plan = await memory.getplan();\n if (plan && [\"pending\", \"approved\"].includes(plan.state)) await memory.setplan({ ...plan, state: \"cancelled\" });\n await audit(\"stop\", \"The user stopped the browser session.\", { ...(session ? { sessionid: session.id } : {}), ...(plan ? { planid: plan.id } : {}) });\n return { stopped: true };\n }\n default: throw new Error(\"Unknown Devthink request.\");\n }\n}\n\nchrome.runtime.onMessage.addListener((message, sender, sendresponse) => {\n handlerequest(message, sender).then(value => sendresponse({ ok: true, value })).catch(error => sendresponse({ ok: false, error: error instanceof Error ? error.message : String(error) }));\n return true;\n});\n\nchrome.runtime.onConnect.addListener(port => {\n if (port.name !== \"devthinksidepanel\" || port.sender?.id !== chrome.runtime.id || !port.sender.url?.startsWith(chrome.runtime.getURL(\"\"))) return port.disconnect();\n port.onMessage.addListener(message => { handlerequest(message, port.sender ?? {}).then(value => port.postMessage({ ok: true, value })).catch(error => port.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) })); });\n});\n"],
5
- "mappings": "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",
3
+ "sources": ["../../memory.ts", "../../policy.ts", "../../progress.ts", "../../version.ts", "../../types.ts", "../../protocol.ts", "../background.ts"],
4
+ "sourcesContent": ["import type { agentplan, agentsession, auditevent, diagnosticreport, endpointconfig, planprogress } from \"./types.js\";\n\n/** Provides a small storage seam that works in browser, tests and future adapters. */\nexport interface memoryadapter {\n get<T>(key: string): Promise<T | undefined>;\n set<T>(key: string, value: T): Promise<void>;\n}\n\nexport class sessionmemory {\n constructor(private readonly adapter: memoryadapter) {}\n\n async getconfig(): Promise<endpointconfig | undefined> { return this.adapter.get<endpointconfig>(\"config\"); }\n async setconfig(value: endpointconfig): Promise<void> { return this.adapter.set(\"config\", value); }\n async getsession(): Promise<agentsession | undefined> { return this.adapter.get<agentsession>(\"session\"); }\n async setsession(value: agentsession): Promise<void> { return this.adapter.set(\"session\", value); }\n async getplan(): Promise<agentplan | undefined> { return this.adapter.get<agentplan>(\"plan\"); }\n async setplan(value: agentplan): Promise<void> { return this.adapter.set(\"plan\", value); }\n async getdiagnostic(): Promise<diagnosticreport | undefined> { return this.adapter.get<diagnosticreport>(\"diagnostic\"); }\n async setdiagnostic(value: diagnosticreport): Promise<void> { return this.adapter.set(\"diagnostic\", value); }\n async getprogress(): Promise<planprogress | undefined> { return this.adapter.get<planprogress>(\"progress\"); }\n async setprogress(value: planprogress): Promise<void> { return this.adapter.set(\"progress\", value); }\n async getaudit(): Promise<auditevent[]> { return (await this.adapter.get<auditevent[]>(\"audit\")) ?? []; }\n async addaudi(event: auditevent): Promise<void> {\n const records = await this.getaudit();\n await this.adapter.set(\"audit\", [event, ...records].slice(0, 100));\n }\n}\n\n/** Creates identifiers locally without a network dependency. */\nexport function randomid(): string {\n return crypto.randomUUID();\n}\n", "import type { actionkind, agentplan, agentsession, endpointconfig, policyevaluation, toolstep } from \"./types.js\";\n\nconst sensitiveactions = new Set<actionkind>([\"click\", \"type\", \"navigate\", \"select\"]);\nconst interactionactions = new Set<actionkind>([\"focus\", \"scroll\", \"hover\"]);\nconst allowedactions = new Set<actionkind>([\"observe\", \"inspect\", \"focus\", \"click\", \"type\", \"navigate\", \"scroll\", \"select\", \"hover\", \"extract\", \"wait\"]);\nconst targetactions = new Set<actionkind>([\"inspect\", \"focus\", \"click\", \"type\", \"scroll\", \"select\", \"hover\"]);\n\n/** Normalizes a user supplied HTTPS endpoint without preserving a provider lock-in. */\nexport function normalizeendpoint(value: string): endpointconfig {\n const endpoint = new URL(value.trim());\n if (endpoint.protocol !== \"https:\") throw new Error(\"Devthink accepts HTTPS endpoints only.\");\n if (endpoint.username || endpoint.password) throw new Error(\"Endpoint credentials are not allowed in the URL.\");\n return { endpoint: endpoint.toString(), origin: endpoint.origin, configuredat: Date.now() };\n}\n\n/** Creates the exact optional host pattern requested from Chromium. */\nexport function hostpattern(origin: string): string {\n const parsed = new URL(origin);\n if (parsed.protocol !== \"https:\") throw new Error(\"Only HTTPS origins can be granted.\");\n return `${parsed.origin}/*`;\n}\n\n/** Defines action risk from a fixed local allowlist. */\nexport function actionrisk(kind: actionkind): \"read\" | \"interaction\" | \"sensitive\" {\n if (!allowedactions.has(kind)) throw new Error(\"Unsupported browser action.\");\n if (sensitiveactions.has(kind)) return \"sensitive\";\n return interactionactions.has(kind) ? \"interaction\" : \"read\";\n}\n\n/** Parses the bounded pause duration of a wait step. */\nexport function waitduration(step: toolstep): number {\n const requested = step.value ? Number.parseInt(step.value, 10) : 250;\n if (!Number.isFinite(requested) || requested < 0) throw new Error(\"Wait duration must be zero or a positive number of milliseconds.\");\n return Math.min(requested, 10_000);\n}\n\n/** Validates a single proposal against the active tab origin and local policy. */\nexport function validatestep(step: toolstep, origin: string): policyevaluation {\n if (!allowedactions.has(step.kind)) return { allowed: false, reason: \"Unsupported action kind.\" };\n if (!step.summary.trim()) return { allowed: false, reason: \"A human-readable action summary is required.\" };\n if (targetactions.has(step.kind) && !step.target?.trim()) return { allowed: false, reason: \"A page target is required.\" };\n if (step.kind === \"select\" && !step.value?.trim()) return { allowed: false, reason: \"A reviewed option value is required.\" };\n if (step.kind === \"wait\") {\n try { waitduration(step); } catch { return { allowed: false, reason: \"Wait duration must be zero or a positive number of milliseconds.\" }; }\n }\n if (step.kind === \"navigate\") {\n if (!step.value) return { allowed: false, reason: \"A navigation URL is required.\" };\n try {\n if (new URL(step.value).origin !== origin) return { allowed: false, reason: \"Navigation must remain within the approved origin.\" };\n } catch {\n return { allowed: false, reason: \"Navigation URL is invalid.\" };\n }\n }\n return { allowed: true };\n}\n\n/** Shared session gate: a live, unpaused session that still matches the active tab. */\nfunction sessiongate(input: { session: agentsession | undefined; tabid: number; origin: string; now: number; action: string }): policyevaluation {\n if (!input.session || input.session.stoppedat) return { allowed: false, reason: \"No active browser session exists.\" };\n if (input.session.expiresat <= input.now) return { allowed: false, reason: \"The browser session has expired.\" };\n if (input.session.pausedat) return { allowed: false, reason: `The browser session is paused and cannot ${input.action}.` };\n if (input.session.tabid !== input.tabid || input.session.origin !== input.origin) return { allowed: false, reason: `The ${input.action} is outside the approved tab or origin.` };\n return { allowed: true };\n}\n\n/** Applies the consent gate immediately before an action reaches the page bridge. */\nexport function canexecute(input: { session: agentsession | undefined; plan: agentplan | undefined; step: toolstep; tabid: number; origin: string; now?: number }): policyevaluation {\n const now = input.now ?? Date.now();\n const gate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: \"execute an action\" });\n if (!gate.allowed) return gate;\n if (!input.plan || input.plan.state !== \"approved\") return { allowed: false, reason: \"The plan has not received explicit approval.\" };\n if (input.plan.expiresat <= now) return { allowed: false, reason: \"The approved plan has expired.\" };\n return validatestep(input.step, input.origin);\n}\n\n/** Allows a non-mutating, temporary target preview during plan review. */\nexport function canpreview(input: { session: agentsession | undefined; plan: agentplan | undefined; step: toolstep; tabid: number; origin: string; now?: number }): policyevaluation {\n const now = input.now ?? Date.now();\n const gate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: \"preview a target\" });\n if (!gate.allowed) return gate;\n if (!input.plan || ![\"pending\", \"approved\"].includes(input.plan.state)) return { allowed: false, reason: \"Only a reviewed pending or approved plan can be previewed.\" };\n if (input.plan.expiresat <= now) return { allowed: false, reason: \"The reviewed plan has expired.\" };\n if (![\"focus\", \"inspect\", \"click\", \"type\", \"scroll\", \"select\", \"hover\"].includes(input.step.kind)) return { allowed: false, reason: \"Only a target-based action can be previewed.\" };\n return validatestep(input.step, input.origin);\n}\n", "import type { agentplan, planprogress } from \"./types.js\";\n\n/**\n * Execution-progress logics for reviewed plans.\n * Every correlated rule for step completion, deduplication and plan closure lives in this file.\n */\n\n/** Returns a fresh progress record for a plan that has not executed any step yet. */\nexport function emptyprogress(planid: string, now: number): planprogress {\n return { planid, completedsteps: [], updatedat: now };\n}\n\n/** Records one successfully executed step; repeated executions of the same step stay deduplicated. */\nexport function recordstep(progress: planprogress | undefined, planid: string, stepid: string, now: number): planprogress {\n const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);\n if (base.completedsteps.includes(stepid)) return { ...base, updatedat: now };\n return { planid, completedsteps: [...base.completedsteps, stepid], updatedat: now };\n}\n\n/** True only when every step of the plan has a recorded, successful execution. */\nexport function iscomplete(progress: planprogress | undefined, plan: agentplan): boolean {\n if (!progress || progress.planid !== plan.id) return false;\n const required = plan.steps.map(step => step.id);\n return required.length > 0 && required.every(id => progress.completedsteps.includes(id));\n}\n\n/** Clears progress whenever a different plan replaces the tracked one. */\nexport function resetforplan(progress: planprogress | undefined, plan: agentplan, now: number): planprogress {\n if (progress && progress.planid === plan.id) return progress;\n return emptyprogress(plan.id, now);\n}\n", "/** Canonical package version synchronized from package.json. */\nexport const packageversion = \"1.1.31\" as const;\n", "/** Shared contracts for every Devthink target. */\nimport { packageversion } from \"./version.js\";\n\nexport const protocolversion = packageversion;\n\nexport type actionkind = \"observe\" | \"inspect\" | \"focus\" | \"click\" | \"type\" | \"navigate\" | \"scroll\" | \"select\" | \"hover\" | \"extract\" | \"wait\";\nexport type actionrisk = \"read\" | \"interaction\" | \"sensitive\";\nexport type planstate = \"draft\" | \"pending\" | \"approved\" | \"rejected\" | \"expired\" | \"completed\" | \"cancelled\";\nexport type auditkind = \"configure\" | \"session\" | \"observe\" | \"proposal\" | \"approval\" | \"action\" | \"error\" | \"stop\" | \"pause\" | \"resume\" | \"complete\";\n\nexport interface toolstep {\n id: string;\n kind: actionkind;\n target?: string;\n value?: string;\n summary: string;\n risk: actionrisk;\n}\n\nexport interface agentplan {\n id: string;\n objective: string;\n origin: string;\n steps: toolstep[];\n createdat: number;\n expiresat: number;\n state: planstate;\n approvedat?: number;\n completedat?: number;\n}\n\nexport interface agentsession {\n id: string;\n tabid: number;\n origin: string;\n startedat: number;\n expiresat: number;\n stoppedat?: number;\n pausedat?: number;\n}\n\nexport interface endpointconfig {\n endpoint: string;\n origin: string;\n configuredat: number;\n}\n\nexport interface observation {\n url: string;\n title: string;\n textpreview: string;\n textlength: number;\n forms: Array<{ label: string; type: string; name: string; options?: string[] }>;\n interactive: Array<{ selector: string; role: string; label: string }>;\n capturedat: number;\n}\n\nexport interface auditevent {\n id: string;\n kind: auditkind;\n at: number;\n summary: string;\n sessionid?: string;\n planid?: string;\n stepid?: string;\n}\n\nexport interface diagnosticreport {\n id: string;\n sessionid: string;\n origin: string;\n capturedat: number;\n tabid: number;\n title: string;\n textlength: number;\n interactivecount: number;\n formcount: number;\n bridgeavailable: boolean;\n}\n\nexport interface proposalrequest {\n objective: string;\n session: agentsession;\n observation: observation;\n}\n\nexport interface planproposal {\n version: typeof protocolversion;\n plan: agentplan;\n}\n\nexport interface policyevaluation {\n allowed: boolean;\n reason?: string;\n}\n\n/** Tracks which reviewed steps of one plan have already executed locally. */\nexport interface planprogress {\n planid: string;\n completedsteps: string[];\n updatedat: number;\n}\n", "import { actionrisk, validatestep } from \"./policy.js\";\nimport { protocolversion, type agentplan, type planproposal, type proposalrequest, type toolstep } from \"./types.js\";\n\nfunction record(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) throw new Error(\"Protocol message must be an object.\");\n return value as Record<string, unknown>;\n}\n\nfunction text(value: unknown, field: string): string {\n if (typeof value !== \"string\" || !value.trim()) throw new Error(`${field} must be a non-empty string.`);\n return value.trim();\n}\n\n/** Validates agent output before it becomes a locally reviewable plan. */\nexport function parseproposal(value: unknown, origin: string): planproposal {\n const root = record(value);\n if (root.version !== protocolversion) throw new Error(\"Unsupported protocol version.\");\n const planinput = record(root.plan);\n const stepsinput = planinput.steps;\n if (!Array.isArray(stepsinput) || stepsinput.length === 0 || stepsinput.length > 50) throw new Error(\"A plan needs between one and fifty steps.\");\n const steps: toolstep[] = stepsinput.map((input, index) => {\n const candidate = record(input);\n const kind = text(candidate.kind, `step ${index + 1} kind`) as toolstep[\"kind\"];\n const step: toolstep = {\n id: typeof candidate.id === \"string\" ? candidate.id : crypto.randomUUID(),\n kind,\n summary: text(candidate.summary, `step ${index + 1} summary`),\n risk: actionrisk(kind),\n ...(typeof candidate.target === \"string\" ? { target: candidate.target } : {}),\n ...(typeof candidate.value === \"string\" ? { value: candidate.value } : {}),\n };\n const evaluation = validatestep(step, origin);\n if (!evaluation.allowed) throw new Error(evaluation.reason);\n return step;\n });\n const createdat = Date.now();\n const plan: agentplan = {\n id: typeof planinput.id === \"string\" ? planinput.id : crypto.randomUUID(),\n objective: text(planinput.objective, \"objective\"),\n origin,\n steps,\n createdat,\n expiresat: Math.min(typeof planinput.expiresat === \"number\" ? planinput.expiresat : createdat + 10 * 60 * 1000, createdat + 30 * 60 * 1000),\n state: \"pending\",\n };\n if (plan.expiresat <= createdat) throw new Error(\"Plan expiry must be in the future.\");\n return { version: protocolversion, plan };\n}\n\n/** Shapes the only data that may be sent to a user-configured agent endpoint. */\nexport function requestbody(input: proposalrequest): string {\n return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation });\n}\n", "import { randomid, sessionmemory } from \"../memory.js\";\nimport { canexecute, canpreview, hostpattern, normalizeendpoint } from \"../policy.js\";\nimport { iscomplete, recordstep, resetforplan } from \"../progress.js\";\nimport { parseproposal, requestbody } from \"../protocol.js\";\nimport type { agentplan, agentsession, auditevent, diagnosticreport, observation, toolstep } from \"../types.js\";\n\nconst sessionduration = 15 * 60 * 1000;\n\nconst chromestorage = {\n async get<T>(key: string): Promise<T | undefined> { return (await chrome.storage.local.get(key))[key] as T | undefined; },\n async set<T>(key: string, value: T): Promise<void> { await chrome.storage.local.set({ [key]: value }); },\n};\nconst memory = new sessionmemory(chromestorage);\n\nfunction extensionpage(sender: chrome.runtime.MessageSender): boolean {\n return sender.id === chrome.runtime.id && Boolean(sender.url?.startsWith(chrome.runtime.getURL(\"\")));\n}\n\nasync function audit(kind: auditevent[\"kind\"], summary: string, extra: Partial<auditevent> = {}): Promise<void> {\n await memory.addaudi({ id: randomid(), kind, at: Date.now(), summary, ...extra });\n}\n\nasync function activecontext(): Promise<{ tab: chrome.tabs.Tab; origin: string }> {\n const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true });\n if (!tab?.id || !tab.url) throw new Error(\"No active web tab is available.\");\n const origin = new URL(tab.url).origin;\n if (!origin.startsWith(\"https://\")) throw new Error(\"Devthink can work with HTTPS pages only.\");\n return { tab, origin };\n}\n\nasync function snapshot(tabid: number): Promise<observation> {\n await chrome.scripting.executeScript({ target: { tabId: tabid }, files: [\"pagebridge.js\"] });\n const result = await chrome.scripting.executeScript({ target: { tabId: tabid }, func: () => {\n const bridge = (globalThis as typeof globalThis & { devthinkbridge?: { capturesnapshot: () => observation } }).devthinkbridge;\n if (!bridge) throw new Error(\"Devthink page bridge is unavailable.\");\n return bridge.capturesnapshot();\n } });\n const value = result[0]?.result;\n if (!value) throw new Error(\"The page did not return an observation.\");\n return value as observation;\n}\n\nasync function startsession(): Promise<agentsession> {\n const { tab, origin } = await activecontext();\n const session: agentsession = { id: randomid(), tabid: tab.id as number, origin, startedat: Date.now(), expiresat: Date.now() + sessionduration };\n await memory.setsession(session);\n await audit(\"session\", `Session started for ${origin}.`, { sessionid: session.id });\n return session;\n}\n\nasync function diagnostic(): Promise<diagnosticreport> {\n const session = await memory.getsession();\n const { tab, origin } = await activecontext();\n if (!session || session.stoppedat || session.expiresat <= Date.now() || session.tabid !== tab.id || session.origin !== origin) throw new Error(\"Start a current session for this active tab before diagnostics.\");\n const observation = await snapshot(session.tabid);\n const report: diagnosticreport = { id: randomid(), sessionid: session.id, origin, capturedat: Date.now(), tabid: session.tabid, title: observation.title, textlength: observation.textlength, interactivecount: observation.interactive.length, formcount: observation.forms.length, bridgeavailable: true };\n await memory.setdiagnostic(report);\n await audit(\"observe\", \"Structured diagnostics captured for the approved active tab.\", { sessionid: session.id });\n return report;\n}\n\nfunction localplan(objective: string, session: agentsession): agentplan {\n const now = Date.now();\n return { id: randomid(), objective, origin: session.origin, steps: [{ id: randomid(), kind: \"observe\", summary: \"Capture a bounded semantic snapshot of the approved active tab.\", risk: \"read\" }], createdat: now, expiresat: now + sessionduration, state: \"pending\" };\n}\n\nasync function propose(objective: string, remote: boolean): Promise<agentplan> {\n if (!objective.trim()) throw new Error(\"An objective is required.\");\n const session = await memory.getsession();\n if (!session || session.stoppedat || session.expiresat <= Date.now()) throw new Error(\"Start a current browser session before requesting a plan.\");\n const { tab, origin } = await activecontext();\n if (session.tabid !== tab.id || session.origin !== origin) throw new Error(\"The selected tab or origin no longer matches the approved session.\");\n const observation = await snapshot(session.tabid);\n const config = await memory.getconfig();\n let plan = localplan(objective.trim(), session);\n if (remote) {\n if (!config) throw new Error(\"Configure an approved HTTPS endpoint before requesting a remote proposal.\");\n const response = await fetch(config.endpoint, { method: \"POST\", headers: { \"content-type\": \"application/json\" }, credentials: \"omit\", body: requestbody({ objective: objective.trim(), session, observation }) });\n if (!response.ok) throw new Error(`Proposal endpoint returned ${response.status}.`);\n plan = parseproposal(await response.json(), session.origin).plan;\n }\n await memory.setplan(plan);\n await memory.setprogress(resetforplan(await memory.getprogress(), plan, Date.now()));\n await audit(\"proposal\", `Plan proposed with ${plan.steps.length} reviewed step${plan.steps.length === 1 ? \"\" : \"s\"}.`, { sessionid: session.id, planid: plan.id });\n return plan;\n}\n\nasync function executestep(stepid: string): Promise<{ ok: boolean; summary: string }> {\n const session = await memory.getsession();\n const plan = await memory.getplan();\n const { tab, origin } = await activecontext();\n const step = plan?.steps.find(candidate => candidate.id === stepid);\n if (!step) throw new Error(\"Reviewed step was not found.\");\n const gate = canexecute({ session, plan, step, tabid: tab.id as number, origin });\n if (!gate.allowed) throw new Error(gate.reason);\n const fresh = await snapshot(tab.id as number);\n if (step.target && !fresh.interactive.some(item => item.selector === step.target)) throw new Error(\"The page changed and the target must be reviewed again.\");\n const result = await chrome.scripting.executeScript({ target: { tabId: tab.id as number }, func: (action: toolstep, expectedorigin: string) => {\n const bridge = (globalThis as typeof globalThis & { devthinkbridge?: { performstep: (input: toolstep, origin: string) => { ok: boolean; summary: string } | Promise<{ ok: boolean; summary: string }> } }).devthinkbridge;\n if (!bridge) throw new Error(\"Devthink page bridge is unavailable.\");\n return bridge.performstep(action, expectedorigin);\n }, args: [step, origin] });\n const output = result[0]?.result as { ok: boolean; summary: string } | undefined;\n const summary = output?.summary ?? \"The page action returned no result.\";\n await audit(output?.ok ? \"action\" : \"error\", summary, { ...(session ? { sessionid: session.id } : {}), ...(plan ? { planid: plan.id } : {}), stepid });\n if (output?.ok && plan) {\n const progress = recordstep(await memory.getprogress(), plan.id, stepid, Date.now());\n await memory.setprogress(progress);\n if (iscomplete(progress, plan) && plan.state === \"approved\") {\n const completed = { ...plan, state: \"completed\" as const, completedat: Date.now() };\n await memory.setplan(completed);\n await audit(\"complete\", \"Every reviewed step of the approved plan has executed.\", { ...(session ? { sessionid: session.id } : {}), planid: completed.id });\n }\n }\n return output ?? { ok: false, summary };\n}\n\n/** Highlights a reviewed target without clicking, typing, navigating or transmitting page data. */\nasync function previewstep(stepid: string): Promise<{ ok: boolean; summary: string }> {\n const session = await memory.getsession();\n const plan = await memory.getplan();\n const { tab, origin } = await activecontext();\n const step = plan?.steps.find(candidate => candidate.id === stepid);\n if (!step) throw new Error(\"Reviewed step was not found.\");\n const gate = canpreview({ session, plan, step, tabid: tab.id as number, origin });\n if (!gate.allowed) throw new Error(gate.reason);\n const fresh = await snapshot(tab.id as number);\n if (!step.target || !fresh.interactive.some(item => item.selector === step.target)) throw new Error(\"The page changed and the target must be reviewed again.\");\n const result = await chrome.scripting.executeScript({ target: { tabId: tab.id as number }, func: (target: string, expectedorigin: string) => {\n const bridge = (globalThis as typeof globalThis & { devthinkbridge?: { previewtarget: (selector: string, origin: string) => { ok: boolean; summary: string } } }).devthinkbridge;\n if (!bridge) throw new Error(\"Devthink page bridge is unavailable.\");\n return bridge.previewtarget(target, expectedorigin);\n }, args: [step.target, origin] });\n const output = result[0]?.result as { ok: boolean; summary: string } | undefined;\n const summary = output?.summary ?? \"The target preview returned no result.\";\n await audit(output?.ok ? \"observe\" : \"error\", summary, { ...(session ? { sessionid: session.id } : {}), ...(plan ? { planid: plan.id } : {}), stepid });\n return output ?? { ok: false, summary };\n}\n\nasync function pausesession(): Promise<agentsession> {\n const session = await memory.getsession();\n if (!session || session.stoppedat) throw new Error(\"No active browser session exists.\");\n if (session.expiresat <= Date.now()) throw new Error(\"The browser session has expired.\");\n if (session.pausedat) throw new Error(\"The browser session is already paused.\");\n const paused = { ...session, pausedat: Date.now() };\n await memory.setsession(paused);\n await audit(\"pause\", \"The user paused the browser session; no action or preview can run.\", { sessionid: session.id });\n return paused;\n}\n\nasync function resumesession(): Promise<agentsession> {\n const session = await memory.getsession();\n if (!session || session.stoppedat) throw new Error(\"No active browser session exists.\");\n if (session.expiresat <= Date.now()) throw new Error(\"The browser session has expired and cannot be resumed.\");\n if (!session.pausedat) throw new Error(\"The browser session is not paused.\");\n const resumed: agentsession = { id: session.id, tabid: session.tabid, origin: session.origin, startedat: session.startedat, expiresat: session.expiresat };\n await memory.setsession(resumed);\n await audit(\"resume\", \"The user resumed the browser session; reviewed actions can run again.\", { sessionid: session.id });\n return resumed;\n}\n\nasync function handlerequest(message: unknown, sender: chrome.runtime.MessageSender): Promise<unknown> {\n if (!extensionpage(sender)) throw new Error(\"Requests are accepted only from Devthink extension pages.\");\n const input = message as { kind?: string; endpoint?: string; objective?: string; stepid?: string };\n switch (input.kind) {\n case \"configure\": {\n const config = normalizeendpoint(input.endpoint ?? \"\");\n const granted = await chrome.permissions.contains({ origins: [hostpattern(config.origin)] });\n if (!granted) throw new Error(\"The endpoint origin has not received optional permission.\");\n await memory.setconfig(config);\n await audit(\"configure\", `Configured user-selected endpoint ${config.origin}.`);\n return config;\n }\n case \"startsession\": return startsession();\n case \"context\": {\n const plan = await memory.getplan();\n const progress = await memory.getprogress();\n return { config: await memory.getconfig(), session: await memory.getsession(), plan, progress: plan && progress?.planid === plan.id ? progress : undefined, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit() };\n }\n case \"diagnostic\": return diagnostic();\n case \"proposelocal\": return propose(input.objective ?? \"\", false);\n case \"proposeremote\": return propose(input.objective ?? \"\", true);\n case \"approve\": {\n const plan = await memory.getplan();\n if (!plan || plan.state !== \"pending\") throw new Error(\"Only a pending plan can be approved.\");\n const approved = { ...plan, state: \"approved\" as const, approvedat: Date.now() };\n await memory.setplan(approved);\n const current = await memory.getsession();\n await audit(\"approval\", \"The user approved the reviewed plan.\", { ...(current ? { sessionid: current.id } : {}), planid: approved.id });\n return approved;\n }\n case \"reject\": {\n const plan = await memory.getplan();\n if (!plan) throw new Error(\"No plan is available to reject.\");\n const rejected = { ...plan, state: \"rejected\" as const };\n await memory.setplan(rejected);\n const current = await memory.getsession();\n await audit(\"approval\", \"The user rejected the plan.\", { ...(current ? { sessionid: current.id } : {}), planid: rejected.id });\n return rejected;\n }\n case \"preview\": return previewstep(input.stepid ?? \"\");\n case \"execute\": return executestep(input.stepid ?? \"\");\n case \"pausesession\": return pausesession();\n case \"resumesession\": return resumesession();\n case \"stop\": {\n const session = await memory.getsession();\n if (session) await memory.setsession({ ...session, stoppedat: Date.now() });\n const plan = await memory.getplan();\n if (plan && [\"pending\", \"approved\"].includes(plan.state)) await memory.setplan({ ...plan, state: \"cancelled\" });\n await audit(\"stop\", \"The user stopped the browser session.\", { ...(session ? { sessionid: session.id } : {}), ...(plan ? { planid: plan.id } : {}) });\n return { stopped: true };\n }\n default: throw new Error(\"Unknown Devthink request.\");\n }\n}\n\nchrome.runtime.onMessage.addListener((message, sender, sendresponse) => {\n handlerequest(message, sender).then(value => sendresponse({ ok: true, value })).catch(error => sendresponse({ ok: false, error: error instanceof Error ? error.message : String(error) }));\n return true;\n});\n\nchrome.runtime.onConnect.addListener(port => {\n if (port.name !== \"devthinksidepanel\" || port.sender?.id !== chrome.runtime.id || !port.sender.url?.startsWith(chrome.runtime.getURL(\"\"))) return port.disconnect();\n port.onMessage.addListener(message => { handlerequest(message, port.sender ?? {}).then(value => port.postMessage({ ok: true, value })).catch(error => port.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) })); });\n});\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 3,
3
3
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnOEjO8Z0PDgQyfvawGcaO2j+o0GLCFTLNj7TkYC/Avo9l2NenMRq7gp90Nfd7E9MViv/OMcCKSYZ5unv12QPRtv31C+a5UQWDFAOP/cH5mwMd6hsayElrSoW8ta+FwFqmr9dIFkn7cQEU3YhZr4Gcbs+ycUHOxVgDA4NBKB0rQ6e9VW5LvTw0isRYUrqM+M72vKxHk9zUIYYn/LGPvottKBYi2GLr0PHSeC2UE+Shmq7vcFIXj6hDjvD4kLJ5sKoUllEcZ1TPuBcnHUQ9ndKA5iktXDQOIJCUJmi7a0YJ2PGg7fvpYfT9k0ai/qZ+pIoRfoOEwE01bPoDn7NjeYnNQIDAQAB",
4
4
  "name": "Devthink",
5
- "version": "1.1.30",
5
+ "version": "1.1.31",
6
6
  "description": "A consent-first bridge for reviewed browser-agent tasks.",
7
7
  "permissions": [
8
8
  "activeTab",
@@ -42,15 +42,58 @@
42
42
  return { ok: true, summary: `Previewing ${label(target) || target.tagName.toLowerCase()} for five seconds.` };
43
43
  }
44
44
  function capturesnapshot() {
45
- const candidates = [...document.querySelectorAll("a[href], button, input, textarea, select, [role=button], [role=link]")].slice(0, 80);
45
+ const candidates = [...document.querySelectorAll("a[href], button, input, textarea, select, [role=button], [role=link], [role=combobox], [role=option], details, summary")].slice(0, 80);
46
46
  const interactive = candidates.map((element) => ({ selector: selector(element), role: element.getAttribute("role") || element.tagName.toLowerCase(), label: label(element) })).filter((item) => item.label || item.role);
47
- const forms = [...document.querySelectorAll("input, textarea, select")].slice(0, 40).map((element) => ({ label: label(element), type: element.getAttribute("type") || element.tagName.toLowerCase(), name: element.getAttribute("name") || "" }));
47
+ const forms = [...document.querySelectorAll("input, textarea, select")].slice(0, 40).map((element) => ({
48
+ label: label(element),
49
+ type: element.getAttribute("type") || element.tagName.toLowerCase(),
50
+ name: element.getAttribute("name") || "",
51
+ ...element instanceof HTMLSelectElement ? { options: [...element.options].slice(0, 12).map((option) => bounded(option.textContent || option.value, 60)) } : {}
52
+ }));
48
53
  const text = bounded(document.body?.innerText || "", 2e3);
49
54
  return { url: location.href, title: bounded(document.title, 180), textpreview: text, textlength: document.body?.innerText.length ?? 0, forms, interactive, capturedat: Date.now() };
50
55
  }
56
+ function extractcontent(targetselector) {
57
+ if (!targetselector) {
58
+ const links = [...document.querySelectorAll("a[href]")].slice(0, 25).map((element) => {
59
+ const href = element instanceof HTMLAnchorElement ? element.getAttribute("href") ?? "" : "";
60
+ return `${bounded(element.textContent || "")} (${bounded(href, 120)})`;
61
+ });
62
+ return { ok: true, summary: `Extracted ${links.length} bounded link entries: ${links.join("; ").slice(0, 180) || "no links present"}.` };
63
+ }
64
+ const target = document.querySelector(targetselector);
65
+ if (!target) return { ok: false, summary: "Extraction target is no longer available." };
66
+ return { ok: true, summary: `Extracted: ${bounded(target.textContent || "", 180) || "empty target"}.` };
67
+ }
68
+ function scrolltarget(target) {
69
+ target.scrollIntoView({ block: "center", inline: "nearest", behavior: "auto" });
70
+ return { ok: true, summary: `Scrolled ${label(target) || target.tagName.toLowerCase()} into view.` };
71
+ }
72
+ function hovertarget(target) {
73
+ for (const type of ["pointerover", "mouseover", "pointerenter"]) {
74
+ target.dispatchEvent(new PointerEvent(type, { bubbles: type !== "pointerenter", cancelable: true, composed: true }));
75
+ }
76
+ target.dispatchEvent(new MouseEvent("mouseenter", { bubbles: false, cancelable: true }));
77
+ return { ok: true, summary: `Hover events delivered to ${label(target) || target.tagName.toLowerCase()}.` };
78
+ }
79
+ function selectoption(target, value) {
80
+ if (!(target instanceof HTMLSelectElement)) return { ok: false, summary: "Target is not a select element." };
81
+ const option = [...target.options].find((candidate) => candidate.value === value || candidate.textContent?.trim() === value);
82
+ if (!option) return { ok: false, summary: "Reviewed option is not part of the select element." };
83
+ target.value = option.value;
84
+ target.dispatchEvent(new Event("input", { bubbles: true }));
85
+ target.dispatchEvent(new Event("change", { bubbles: true }));
86
+ return { ok: true, summary: `Selected ${bounded(option.textContent || option.value, 60)}.` };
87
+ }
51
88
  function performstep(step, expectedorigin) {
52
89
  if (location.origin !== expectedorigin) return { ok: false, summary: "Page origin changed before action." };
53
90
  if (step.kind === "observe") return { ok: true, summary: "Observation completed." };
91
+ if (step.kind === "wait") {
92
+ const requested = step.value ? Number.parseInt(step.value, 10) : 250;
93
+ const duration = Number.isFinite(requested) && requested > 0 ? Math.min(requested, 1e4) : 0;
94
+ return new Promise((resolve) => window.setTimeout(() => resolve({ ok: true, summary: `Bounded wait of ${duration} milliseconds completed.` }), duration));
95
+ }
96
+ if (step.kind === "extract") return extractcontent(step.target);
54
97
  if (step.kind === "navigate") {
55
98
  if (!step.value || new URL(step.value).origin !== expectedorigin) return { ok: false, summary: "Navigation target is outside the approved origin." };
56
99
  location.assign(step.value);
@@ -68,6 +111,9 @@
68
111
  target.click();
69
112
  return { ok: true, summary: "Reviewed click completed." };
70
113
  }
114
+ if (step.kind === "scroll") return scrolltarget(target);
115
+ if (step.kind === "hover") return hovertarget(target);
116
+ if (step.kind === "select") return selectoption(target, step.value ?? "");
71
117
  if (step.kind === "type") {
72
118
  if (!(target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement)) return { ok: false, summary: "Target cannot receive text." };
73
119
  if (typeof step.value !== "string") return { ok: false, summary: "Approved text is absent." };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../pagebridge.ts"],
4
- "sourcesContent": ["import type { observation, toolstep } from \"../types.js\";\n\nfunction bounded(value: string, length = 180): string {\n return value.replace(/\\s+/g, \" \").trim().slice(0, length);\n}\n\nfunction label(element: Element): string {\n const aria = element.getAttribute(\"aria-label\");\n const labelledby = element.getAttribute(\"aria-labelledby\");\n const linked = labelledby ? document.getElementById(labelledby)?.textContent : \"\";\n return bounded(aria || linked || element.getAttribute(\"title\") || element.textContent || \"\");\n}\n\nfunction selector(element: Element): string {\n if (element.id) return `#${CSS.escape(element.id)}`;\n const role = element.getAttribute(\"role\");\n const name = element.getAttribute(\"name\");\n if (role && name) return `[role=\"${CSS.escape(role)}\"][name=\"${CSS.escape(name)}\"]`;\n if (name) return `${element.tagName.toLowerCase()}[name=\"${CSS.escape(name)}\"]`;\n const tag = element.tagName.toLowerCase();\n const parent = element.parentElement;\n if (!parent) return tag;\n const peers = [...parent.children].filter(node => node.tagName === element.tagName);\n return `${tag}:nth-of-type(${peers.indexOf(element) + 1})`;\n}\n\nconst previewid = \"devthinktargetpreview\";\n\nfunction clearpreview(): void {\n document.getElementById(previewid)?.remove();\n}\n\n/** Shows an ephemeral outline only; it neither mutates page data nor dispatches page events. */\nexport function previewtarget(targetselector: string, expectedorigin: string): { ok: boolean; summary: string } {\n if (location.origin !== expectedorigin) return { ok: false, summary: \"Page origin changed before preview.\" };\n clearpreview();\n const target = document.querySelector(targetselector);\n if (!(target instanceof HTMLElement)) return { ok: false, summary: \"Reviewed target is no longer available.\" };\n const rect = target.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return { ok: false, summary: \"Reviewed target is not currently visible.\" };\n const overlay = document.createElement(\"div\");\n overlay.id = previewid;\n overlay.setAttribute(\"aria-hidden\", \"true\");\n Object.assign(overlay.style, { position: \"fixed\", left: `${Math.max(0, rect.left - 3)}px`, top: `${Math.max(0, rect.top - 3)}px`, width: `${rect.width + 6}px`, height: `${rect.height + 6}px`, border: \"3px solid #2f80ed\", borderRadius: \"6px\", boxShadow: \"0 0 0 3px rgba(47,128,237,.28)\", pointerEvents: \"none\", zIndex: \"2147483647\", boxSizing: \"border-box\" });\n document.documentElement.append(overlay);\n window.setTimeout(clearpreview, 5000);\n return { ok: true, summary: `Previewing ${label(target) || target.tagName.toLowerCase()} for five seconds.` };\n}\n\n/** Captures only semantic, bounded page context for a user-approved active tab. */\nexport function capturesnapshot(): observation {\n const candidates = [...document.querySelectorAll(\"a[href], button, input, textarea, select, [role=button], [role=link]\")].slice(0, 80);\n const interactive = candidates.map(element => ({ selector: selector(element), role: element.getAttribute(\"role\") || element.tagName.toLowerCase(), label: label(element) })).filter(item => item.label || item.role);\n const forms = [...document.querySelectorAll(\"input, textarea, select\")].slice(0, 40).map(element => ({ label: label(element), type: element.getAttribute(\"type\") || element.tagName.toLowerCase(), name: element.getAttribute(\"name\") || \"\" }));\n const text = bounded(document.body?.innerText || \"\", 2000);\n return { url: location.href, title: bounded(document.title, 180), textpreview: text, textlength: document.body?.innerText.length ?? 0, forms, interactive, capturedat: Date.now() };\n}\n\n/** Performs one local action after the background policy gate and a fresh target check. */\nexport function performstep(step: toolstep, expectedorigin: string): { ok: boolean; summary: string } {\n if (location.origin !== expectedorigin) return { ok: false, summary: \"Page origin changed before action.\" };\n if (step.kind === \"observe\") return { ok: true, summary: \"Observation completed.\" };\n if (step.kind === \"navigate\") {\n if (!step.value || new URL(step.value).origin !== expectedorigin) return { ok: false, summary: \"Navigation target is outside the approved origin.\" };\n location.assign(step.value);\n return { ok: true, summary: \"Navigation request sent.\" };\n }\n if (!step.target) return { ok: false, summary: \"Action target is absent.\" };\n const target = document.querySelector(step.target);\n if (!(target instanceof HTMLElement)) return { ok: false, summary: \"Action target is no longer available.\" };\n if (step.kind === \"focus\") { target.focus(); return { ok: true, summary: \"Target focused.\" }; }\n if (step.kind === \"inspect\") return { ok: true, summary: `Target: ${label(target) || target.tagName.toLowerCase()}.` };\n if (step.kind === \"click\") { target.click(); return { ok: true, summary: \"Reviewed click completed.\" }; }\n if (step.kind === \"type\") {\n if (!(target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement)) return { ok: false, summary: \"Target cannot receive text.\" };\n if (typeof step.value !== \"string\") return { ok: false, summary: \"Approved text is absent.\" };\n target.focus();\n target.value = step.value;\n target.dispatchEvent(new Event(\"input\", { bubbles: true }));\n target.dispatchEvent(new Event(\"change\", { bubbles: true }));\n return { ok: true, summary: \"Approved text entered.\" };\n }\n return { ok: false, summary: \"Unsupported action.\" };\n}\n\nObject.assign(globalThis, { devthinkbridge: { capturesnapshot, previewtarget, performstep } });\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { observation, toolstep } from \"../types.js\";\n\nfunction bounded(value: string, length = 180): string {\n return value.replace(/\\s+/g, \" \").trim().slice(0, length);\n}\n\nfunction label(element: Element): string {\n const aria = element.getAttribute(\"aria-label\");\n const labelledby = element.getAttribute(\"aria-labelledby\");\n const linked = labelledby ? document.getElementById(labelledby)?.textContent : \"\";\n return bounded(aria || linked || element.getAttribute(\"title\") || element.textContent || \"\");\n}\n\nfunction selector(element: Element): string {\n if (element.id) return `#${CSS.escape(element.id)}`;\n const role = element.getAttribute(\"role\");\n const name = element.getAttribute(\"name\");\n if (role && name) return `[role=\"${CSS.escape(role)}\"][name=\"${CSS.escape(name)}\"]`;\n if (name) return `${element.tagName.toLowerCase()}[name=\"${CSS.escape(name)}\"]`;\n const tag = element.tagName.toLowerCase();\n const parent = element.parentElement;\n if (!parent) return tag;\n const peers = [...parent.children].filter(node => node.tagName === element.tagName);\n return `${tag}:nth-of-type(${peers.indexOf(element) + 1})`;\n}\n\nconst previewid = \"devthinktargetpreview\";\n\nfunction clearpreview(): void {\n document.getElementById(previewid)?.remove();\n}\n\n/** Shows an ephemeral outline only; it neither mutates page data nor dispatches page events. */\nexport function previewtarget(targetselector: string, expectedorigin: string): { ok: boolean; summary: string } {\n if (location.origin !== expectedorigin) return { ok: false, summary: \"Page origin changed before preview.\" };\n clearpreview();\n const target = document.querySelector(targetselector);\n if (!(target instanceof HTMLElement)) return { ok: false, summary: \"Reviewed target is no longer available.\" };\n const rect = target.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return { ok: false, summary: \"Reviewed target is not currently visible.\" };\n const overlay = document.createElement(\"div\");\n overlay.id = previewid;\n overlay.setAttribute(\"aria-hidden\", \"true\");\n Object.assign(overlay.style, { position: \"fixed\", left: `${Math.max(0, rect.left - 3)}px`, top: `${Math.max(0, rect.top - 3)}px`, width: `${rect.width + 6}px`, height: `${rect.height + 6}px`, border: \"3px solid #2f80ed\", borderRadius: \"6px\", boxShadow: \"0 0 0 3px rgba(47,128,237,.28)\", pointerEvents: \"none\", zIndex: \"2147483647\", boxSizing: \"border-box\" });\n document.documentElement.append(overlay);\n window.setTimeout(clearpreview, 5000);\n return { ok: true, summary: `Previewing ${label(target) || target.tagName.toLowerCase()} for five seconds.` };\n}\n\n/** Captures only semantic, bounded page context for a user-approved active tab. */\nexport function capturesnapshot(): observation {\n const candidates = [...document.querySelectorAll(\"a[href], button, input, textarea, select, [role=button], [role=link], [role=combobox], [role=option], details, summary\")].slice(0, 80);\n const interactive = candidates.map(element => ({ selector: selector(element), role: element.getAttribute(\"role\") || element.tagName.toLowerCase(), label: label(element) })).filter(item => item.label || item.role);\n const forms = [...document.querySelectorAll(\"input, textarea, select\")].slice(0, 40).map(element => ({\n label: label(element),\n type: element.getAttribute(\"type\") || element.tagName.toLowerCase(),\n name: element.getAttribute(\"name\") || \"\",\n ...(element instanceof HTMLSelectElement ? { options: [...element.options].slice(0, 12).map(option => bounded(option.textContent || option.value, 60)) } : {}),\n }));\n const text = bounded(document.body?.innerText || \"\", 2000);\n return { url: location.href, title: bounded(document.title, 180), textpreview: text, textlength: document.body?.innerText.length ?? 0, forms, interactive, capturedat: Date.now() };\n}\n\n/** Extracts bounded, read-only structured content for a reviewed extraction step. */\nfunction extractcontent(targetselector: string | undefined): { ok: boolean; summary: string } {\n if (!targetselector) {\n const links = [...document.querySelectorAll(\"a[href]\")].slice(0, 25).map(element => {\n const href = element instanceof HTMLAnchorElement ? element.getAttribute(\"href\") ?? \"\" : \"\";\n return `${bounded(element.textContent || \"\")} (${bounded(href, 120)})`;\n });\n return { ok: true, summary: `Extracted ${links.length} bounded link entries: ${links.join(\"; \").slice(0, 180) || \"no links present\"}.` };\n }\n const target = document.querySelector(targetselector);\n if (!target) return { ok: false, summary: \"Extraction target is no longer available.\" };\n return { ok: true, summary: `Extracted: ${bounded(target.textContent || \"\", 180) || \"empty target\"}.` };\n}\n\n/** Scrolls one reviewed target into view without reading or changing other page state. */\nfunction scrolltarget(target: HTMLElement): { ok: boolean; summary: string } {\n target.scrollIntoView({ block: \"center\", inline: \"nearest\", behavior: \"auto\" });\n return { ok: true, summary: `Scrolled ${label(target) || target.tagName.toLowerCase()} into view.` };\n}\n\n/** Dispatches bounded hover events on one reviewed target. */\nfunction hovertarget(target: HTMLElement): { ok: boolean; summary: string } {\n for (const type of [\"pointerover\", \"mouseover\", \"pointerenter\"] as const) {\n target.dispatchEvent(new PointerEvent(type, { bubbles: type !== \"pointerenter\", cancelable: true, composed: true }));\n }\n target.dispatchEvent(new MouseEvent(\"mouseenter\", { bubbles: false, cancelable: true }));\n return { ok: true, summary: `Hover events delivered to ${label(target) || target.tagName.toLowerCase()}.` };\n}\n\n/** Selects one reviewed existing option; values outside the declared options are refused. */\nfunction selectoption(target: HTMLElement, value: string): { ok: boolean; summary: string } {\n if (!(target instanceof HTMLSelectElement)) return { ok: false, summary: \"Target is not a select element.\" };\n const option = [...target.options].find(candidate => candidate.value === value || candidate.textContent?.trim() === value);\n if (!option) return { ok: false, summary: \"Reviewed option is not part of the select element.\" };\n target.value = option.value;\n target.dispatchEvent(new Event(\"input\", { bubbles: true }));\n target.dispatchEvent(new Event(\"change\", { bubbles: true }));\n return { ok: true, summary: `Selected ${bounded(option.textContent || option.value, 60)}.` };\n}\n\n/** Performs one local action after the background policy gate and a fresh target check. */\nexport function performstep(step: toolstep, expectedorigin: string): { ok: boolean; summary: string } | Promise<{ ok: boolean; summary: string }> {\n if (location.origin !== expectedorigin) return { ok: false, summary: \"Page origin changed before action.\" };\n if (step.kind === \"observe\") return { ok: true, summary: \"Observation completed.\" };\n if (step.kind === \"wait\") {\n const requested = step.value ? Number.parseInt(step.value, 10) : 250;\n const duration = Number.isFinite(requested) && requested > 0 ? Math.min(requested, 10_000) : 0;\n return new Promise(resolve => window.setTimeout(() => resolve({ ok: true, summary: `Bounded wait of ${duration} milliseconds completed.` }), duration));\n }\n if (step.kind === \"extract\") return extractcontent(step.target);\n if (step.kind === \"navigate\") {\n if (!step.value || new URL(step.value).origin !== expectedorigin) return { ok: false, summary: \"Navigation target is outside the approved origin.\" };\n location.assign(step.value);\n return { ok: true, summary: \"Navigation request sent.\" };\n }\n if (!step.target) return { ok: false, summary: \"Action target is absent.\" };\n const target = document.querySelector(step.target);\n if (!(target instanceof HTMLElement)) return { ok: false, summary: \"Action target is no longer available.\" };\n if (step.kind === \"focus\") { target.focus(); return { ok: true, summary: \"Target focused.\" }; }\n if (step.kind === \"inspect\") return { ok: true, summary: `Target: ${label(target) || target.tagName.toLowerCase()}.` };\n if (step.kind === \"click\") { target.click(); return { ok: true, summary: \"Reviewed click completed.\" }; }\n if (step.kind === \"scroll\") return scrolltarget(target);\n if (step.kind === \"hover\") return hovertarget(target);\n if (step.kind === \"select\") return selectoption(target, step.value ?? \"\");\n if (step.kind === \"type\") {\n if (!(target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement)) return { ok: false, summary: \"Target cannot receive text.\" };\n if (typeof step.value !== \"string\") return { ok: false, summary: \"Approved text is absent.\" };\n target.focus();\n target.value = step.value;\n target.dispatchEvent(new Event(\"input\", { bubbles: true }));\n target.dispatchEvent(new Event(\"change\", { bubbles: true }));\n return { ok: true, summary: \"Approved text entered.\" };\n }\n return { ok: false, summary: \"Unsupported action.\" };\n}\n\nObject.assign(globalThis, { devthinkbridge: { capturesnapshot, previewtarget, performstep } });\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,5 @@
1
1
  <!doctype html>
2
2
  <html lang="en">
3
3
  <head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Devthink</title><link rel="stylesheet" href="style.css"></head>
4
- <body><main><header><p class="eyebrow">DEVTHINK 1.1.30</p><h1>Browser bridge</h1><p>Consent is required before page access or action.</p></header><label for="endpoint">Optional agent endpoint</label><input id="endpoint" type="url" inputmode="url" placeholder="https://agent.example/proposal" autocomplete="off"><button id="connect">Approve endpoint origin</button><div class="actions"><button id="session">Start active tab session</button><button id="openpanel" class="secondary">Review plans</button><button id="stop" class="danger">Stop</button></div><p id="status" role="status">Loading session state.</p></main><script type="module" src="popup.js"></script></body>
4
+ <body><main><header><p class="eyebrow">DEVTHINK 1.1.31</p><h1>Browser bridge</h1><p>Consent is required before page access or action.</p></header><label for="endpoint">Optional agent endpoint</label><input id="endpoint" type="url" inputmode="url" placeholder="https://agent.example/proposal" autocomplete="off"><button id="connect">Approve endpoint origin</button><div class="actions"><button id="session">Start active tab session</button><button id="pause" class="secondary">Pause session</button><button id="openpanel" class="secondary">Review plans</button><button id="stop" class="danger">Stop</button></div><p id="status" role="status">Loading session state.</p></main><script type="module" src="popup.js"></script></body>
5
5
  </html>
@@ -16,6 +16,7 @@ var endpointinput = document.querySelector("#endpoint");
16
16
  var statusnode = document.querySelector("#status");
17
17
  var connectbutton = document.querySelector("#connect");
18
18
  var sessionbutton = document.querySelector("#session");
19
+ var pausebutton = document.querySelector("#pause");
19
20
  var stopbutton = document.querySelector("#stop");
20
21
  var openbutton = document.querySelector("#openpanel");
21
22
  function status(message, error = false) {
@@ -29,11 +30,19 @@ async function request(message) {
29
30
  if (!response.ok) throw new Error(response.error);
30
31
  return response.value;
31
32
  }
33
+ function pauselabel(paused) {
34
+ return paused ? "Resume session" : "Pause session";
35
+ }
32
36
  async function restore() {
33
37
  const context = await request({ kind: "context" });
34
38
  if (endpointinput && context.config) endpointinput.value = context.config.endpoint;
35
39
  const active = context.session && !context.session.stoppedat && context.session.expiresat > Date.now();
36
- status(active ? "Session active. Review the plan in the side panel." : "No active browser session.");
40
+ if (pausebutton) {
41
+ pausebutton.disabled = !active;
42
+ pausebutton.textContent = pauselabel(Boolean(context.session?.pausedat));
43
+ }
44
+ if (active && context.session?.pausedat) status("Session paused. Reviewed actions are blocked until resume.");
45
+ else status(active ? "Session active. Review the plan in the side panel." : "No active browser session.");
37
46
  }
38
47
  connectbutton?.addEventListener("click", async () => {
39
48
  try {
@@ -50,6 +59,16 @@ sessionbutton?.addEventListener("click", async () => {
50
59
  try {
51
60
  await request({ kind: "startsession" });
52
61
  status("Session started for the active HTTPS tab.");
62
+ await restore();
63
+ } catch (error) {
64
+ status(error instanceof Error ? error.message : String(error), true);
65
+ }
66
+ });
67
+ pausebutton?.addEventListener("click", async () => {
68
+ try {
69
+ const context = await request({ kind: "context" });
70
+ await request({ kind: context.session?.pausedat ? "resumesession" : "pausesession" });
71
+ await restore();
53
72
  } catch (error) {
54
73
  status(error instanceof Error ? error.message : String(error), true);
55
74
  }
@@ -58,6 +77,7 @@ stopbutton?.addEventListener("click", async () => {
58
77
  try {
59
78
  await request({ kind: "stop" });
60
79
  status("Session stopped. No action can continue.");
80
+ await restore();
61
81
  } catch (error) {
62
82
  status(error instanceof Error ? error.message : String(error), true);
63
83
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../policy.ts", "../popup.ts"],
4
- "sourcesContent": ["import type { actionkind, agentplan, agentsession, endpointconfig, policyevaluation, toolstep } from \"./types.js\";\n\nconst sensitiveactions = new Set<actionkind>([\"click\", \"type\", \"navigate\"]);\nconst allowedactions = new Set<actionkind>([\"observe\", \"inspect\", \"focus\", \"click\", \"type\", \"navigate\"]);\n\n/** Normalizes a user supplied HTTPS endpoint without preserving a provider lock-in. */\nexport function normalizeendpoint(value: string): endpointconfig {\n const endpoint = new URL(value.trim());\n if (endpoint.protocol !== \"https:\") throw new Error(\"Devthink accepts HTTPS endpoints only.\");\n if (endpoint.username || endpoint.password) throw new Error(\"Endpoint credentials are not allowed in the URL.\");\n return { endpoint: endpoint.toString(), origin: endpoint.origin, configuredat: Date.now() };\n}\n\n/** Creates the exact optional host pattern requested from Chromium. */\nexport function hostpattern(origin: string): string {\n const parsed = new URL(origin);\n if (parsed.protocol !== \"https:\") throw new Error(\"Only HTTPS origins can be granted.\");\n return `${parsed.origin}/*`;\n}\n\n/** Defines action risk from a fixed local allowlist. */\nexport function actionrisk(kind: actionkind): \"read\" | \"interaction\" | \"sensitive\" {\n if (!allowedactions.has(kind)) throw new Error(\"Unsupported browser action.\");\n if (sensitiveactions.has(kind)) return \"sensitive\";\n return kind === \"focus\" ? \"interaction\" : \"read\";\n}\n\n/** Validates a single proposal against the active tab origin and local policy. */\nexport function validatestep(step: toolstep, origin: string): policyevaluation {\n if (!allowedactions.has(step.kind)) return { allowed: false, reason: \"Unsupported action kind.\" };\n if (!step.summary.trim()) return { allowed: false, reason: \"A human-readable action summary is required.\" };\n if ((step.kind === \"click\" || step.kind === \"focus\" || step.kind === \"inspect\" || step.kind === \"type\") && !step.target?.trim()) return { allowed: false, reason: \"A page target is required.\" };\n if (step.kind === \"navigate\") {\n if (!step.value) return { allowed: false, reason: \"A navigation URL is required.\" };\n try {\n if (new URL(step.value).origin !== origin) return { allowed: false, reason: \"Navigation must remain within the approved origin.\" };\n } catch {\n return { allowed: false, reason: \"Navigation URL is invalid.\" };\n }\n }\n return { allowed: true };\n}\n\n/** Applies the consent gate immediately before an action reaches the page bridge. */\nexport function canexecute(input: { session: agentsession | undefined; plan: agentplan | undefined; step: toolstep; tabid: number; origin: string; now?: number }): policyevaluation {\n const now = input.now ?? Date.now();\n if (!input.session || input.session.stoppedat) return { allowed: false, reason: \"No active browser session exists.\" };\n if (input.session.expiresat <= now) return { allowed: false, reason: \"The browser session has expired.\" };\n if (input.session.tabid !== input.tabid || input.session.origin !== input.origin) return { allowed: false, reason: \"The action is outside the approved tab or origin.\" };\n if (!input.plan || input.plan.state !== \"approved\") return { allowed: false, reason: \"The plan has not received explicit approval.\" };\n if (input.plan.expiresat <= now) return { allowed: false, reason: \"The approved plan has expired.\" };\n return validatestep(input.step, input.origin);\n}\n\n/** Allows a non-mutating, temporary target preview during plan review. */\nexport function canpreview(input: { session: agentsession | undefined; plan: agentplan | undefined; step: toolstep; tabid: number; origin: string; now?: number }): policyevaluation {\n const now = input.now ?? Date.now();\n if (!input.session || input.session.stoppedat) return { allowed: false, reason: \"No active browser session exists.\" };\n if (input.session.expiresat <= now) return { allowed: false, reason: \"The browser session has expired.\" };\n if (input.session.tabid !== input.tabid || input.session.origin !== input.origin) return { allowed: false, reason: \"The preview is outside the approved tab or origin.\" };\n if (!input.plan || ![\"pending\", \"approved\"].includes(input.plan.state)) return { allowed: false, reason: \"Only a reviewed pending or approved plan can be previewed.\" };\n if (input.plan.expiresat <= now) return { allowed: false, reason: \"The reviewed plan has expired.\" };\n if (![\"focus\", \"inspect\", \"click\", \"type\"].includes(input.step.kind)) return { allowed: false, reason: \"Only a target-based action can be previewed.\" };\n return validatestep(input.step, input.origin);\n}\n", "import { hostpattern, normalizeendpoint } from \"../policy.js\";\n\nconst endpointinput = document.querySelector<HTMLInputElement>(\"#endpoint\");\nconst statusnode = document.querySelector<HTMLElement>(\"#status\");\nconst connectbutton = document.querySelector<HTMLButtonElement>(\"#connect\");\nconst sessionbutton = document.querySelector<HTMLButtonElement>(\"#session\");\nconst stopbutton = document.querySelector<HTMLButtonElement>(\"#stop\");\nconst openbutton = document.querySelector<HTMLButtonElement>(\"#openpanel\");\n\nfunction status(message: string, error = false): void { if (statusnode) { statusnode.textContent = message; statusnode.dataset.state = error ? \"error\" : \"ready\"; } }\nasync function request(message: unknown): Promise<unknown> { const response = await chrome.runtime.sendMessage(message) as { ok: boolean; value?: unknown; error?: string }; if (!response.ok) throw new Error(response.error); return response.value; }\n\nasync function restore(): Promise<void> {\n const context = await request({ kind: \"context\" }) as { config?: { endpoint: string }; session?: { stoppedat?: number; expiresat: number } };\n if (endpointinput && context.config) endpointinput.value = context.config.endpoint;\n const active = context.session && !context.session.stoppedat && context.session.expiresat > Date.now();\n status(active ? \"Session active. Review the plan in the side panel.\" : \"No active browser session.\");\n}\n\nconnectbutton?.addEventListener(\"click\", async () => {\n try {\n const config = normalizeendpoint(endpointinput?.value ?? \"\");\n const granted = await chrome.permissions.request({ origins: [hostpattern(config.origin)] });\n if (!granted) throw new Error(\"Origin permission was not granted.\");\n await request({ kind: \"configure\", endpoint: config.endpoint });\n status(`Endpoint approved for ${config.origin}.`);\n } catch (error) { status(error instanceof Error ? error.message : String(error), true); }\n});\nsessionbutton?.addEventListener(\"click\", async () => { try { await request({ kind: \"startsession\" }); status(\"Session started for the active HTTPS tab.\"); } catch (error) { status(error instanceof Error ? error.message : String(error), true); } });\nstopbutton?.addEventListener(\"click\", async () => { try { await request({ kind: \"stop\" }); status(\"Session stopped. No action can continue.\"); } catch (error) { status(error instanceof Error ? error.message : String(error), true); } });\nopenbutton?.addEventListener(\"click\", () => chrome.sidePanel.open({ windowId: chrome.windows.WINDOW_ID_CURRENT }));\nrestore().catch(error => status(error instanceof Error ? error.message : String(error), true));\n"],
5
- "mappings": ";AAMO,SAAS,kBAAkB,OAA+B;AAC/D,QAAM,WAAW,IAAI,IAAI,MAAM,KAAK,CAAC;AACrC,MAAI,SAAS,aAAa,SAAU,OAAM,IAAI,MAAM,wCAAwC;AAC5F,MAAI,SAAS,YAAY,SAAS,SAAU,OAAM,IAAI,MAAM,kDAAkD;AAC9G,SAAO,EAAE,UAAU,SAAS,SAAS,GAAG,QAAQ,SAAS,QAAQ,cAAc,KAAK,IAAI,EAAE;AAC5F;AAGO,SAAS,YAAY,QAAwB;AAClD,QAAM,SAAS,IAAI,IAAI,MAAM;AAC7B,MAAI,OAAO,aAAa,SAAU,OAAM,IAAI,MAAM,oCAAoC;AACtF,SAAO,GAAG,OAAO,MAAM;AACzB;;;AChBA,IAAM,gBAAgB,SAAS,cAAgC,WAAW;AAC1E,IAAM,aAAa,SAAS,cAA2B,SAAS;AAChE,IAAM,gBAAgB,SAAS,cAAiC,UAAU;AAC1E,IAAM,gBAAgB,SAAS,cAAiC,UAAU;AAC1E,IAAM,aAAa,SAAS,cAAiC,OAAO;AACpE,IAAM,aAAa,SAAS,cAAiC,YAAY;AAEzE,SAAS,OAAO,SAAiB,QAAQ,OAAa;AAAE,MAAI,YAAY;AAAE,eAAW,cAAc;AAAS,eAAW,QAAQ,QAAQ,QAAQ,UAAU;AAAA,EAAS;AAAE;AACpK,eAAe,QAAQ,SAAoC;AAAE,QAAM,WAAW,MAAM,OAAO,QAAQ,YAAY,OAAO;AAAuD,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,SAAS,KAAK;AAAG,SAAO,SAAS;AAAO;AAEvP,eAAe,UAAyB;AACtC,QAAM,UAAU,MAAM,QAAQ,EAAE,MAAM,UAAU,CAAC;AACjD,MAAI,iBAAiB,QAAQ,OAAQ,eAAc,QAAQ,QAAQ,OAAO;AAC1E,QAAM,SAAS,QAAQ,WAAW,CAAC,QAAQ,QAAQ,aAAa,QAAQ,QAAQ,YAAY,KAAK,IAAI;AACrG,SAAO,SAAS,uDAAuD,4BAA4B;AACrG;AAEA,eAAe,iBAAiB,SAAS,YAAY;AACnD,MAAI;AACF,UAAM,SAAS,kBAAkB,eAAe,SAAS,EAAE;AAC3D,UAAM,UAAU,MAAM,OAAO,YAAY,QAAQ,EAAE,SAAS,CAAC,YAAY,OAAO,MAAM,CAAC,EAAE,CAAC;AAC1F,QAAI,CAAC,QAAS,OAAM,IAAI,MAAM,oCAAoC;AAClE,UAAM,QAAQ,EAAE,MAAM,aAAa,UAAU,OAAO,SAAS,CAAC;AAC9D,WAAO,yBAAyB,OAAO,MAAM,GAAG;AAAA,EAClD,SAAS,OAAO;AAAE,WAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG,IAAI;AAAA,EAAG;AAC1F,CAAC;AACD,eAAe,iBAAiB,SAAS,YAAY;AAAE,MAAI;AAAE,UAAM,QAAQ,EAAE,MAAM,eAAe,CAAC;AAAG,WAAO,2CAA2C;AAAA,EAAG,SAAS,OAAO;AAAE,WAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG,IAAI;AAAA,EAAG;AAAE,CAAC;AACtP,YAAY,iBAAiB,SAAS,YAAY;AAAE,MAAI;AAAE,UAAM,QAAQ,EAAE,MAAM,OAAO,CAAC;AAAG,WAAO,0CAA0C;AAAA,EAAG,SAAS,OAAO;AAAE,WAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG,IAAI;AAAA,EAAG;AAAE,CAAC;AAC1O,YAAY,iBAAiB,SAAS,MAAM,OAAO,UAAU,KAAK,EAAE,UAAU,OAAO,QAAQ,kBAAkB,CAAC,CAAC;AACjH,QAAQ,EAAE,MAAM,WAAS,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG,IAAI,CAAC;",
4
+ "sourcesContent": ["import type { actionkind, agentplan, agentsession, endpointconfig, policyevaluation, toolstep } from \"./types.js\";\n\nconst sensitiveactions = new Set<actionkind>([\"click\", \"type\", \"navigate\", \"select\"]);\nconst interactionactions = new Set<actionkind>([\"focus\", \"scroll\", \"hover\"]);\nconst allowedactions = new Set<actionkind>([\"observe\", \"inspect\", \"focus\", \"click\", \"type\", \"navigate\", \"scroll\", \"select\", \"hover\", \"extract\", \"wait\"]);\nconst targetactions = new Set<actionkind>([\"inspect\", \"focus\", \"click\", \"type\", \"scroll\", \"select\", \"hover\"]);\n\n/** Normalizes a user supplied HTTPS endpoint without preserving a provider lock-in. */\nexport function normalizeendpoint(value: string): endpointconfig {\n const endpoint = new URL(value.trim());\n if (endpoint.protocol !== \"https:\") throw new Error(\"Devthink accepts HTTPS endpoints only.\");\n if (endpoint.username || endpoint.password) throw new Error(\"Endpoint credentials are not allowed in the URL.\");\n return { endpoint: endpoint.toString(), origin: endpoint.origin, configuredat: Date.now() };\n}\n\n/** Creates the exact optional host pattern requested from Chromium. */\nexport function hostpattern(origin: string): string {\n const parsed = new URL(origin);\n if (parsed.protocol !== \"https:\") throw new Error(\"Only HTTPS origins can be granted.\");\n return `${parsed.origin}/*`;\n}\n\n/** Defines action risk from a fixed local allowlist. */\nexport function actionrisk(kind: actionkind): \"read\" | \"interaction\" | \"sensitive\" {\n if (!allowedactions.has(kind)) throw new Error(\"Unsupported browser action.\");\n if (sensitiveactions.has(kind)) return \"sensitive\";\n return interactionactions.has(kind) ? \"interaction\" : \"read\";\n}\n\n/** Parses the bounded pause duration of a wait step. */\nexport function waitduration(step: toolstep): number {\n const requested = step.value ? Number.parseInt(step.value, 10) : 250;\n if (!Number.isFinite(requested) || requested < 0) throw new Error(\"Wait duration must be zero or a positive number of milliseconds.\");\n return Math.min(requested, 10_000);\n}\n\n/** Validates a single proposal against the active tab origin and local policy. */\nexport function validatestep(step: toolstep, origin: string): policyevaluation {\n if (!allowedactions.has(step.kind)) return { allowed: false, reason: \"Unsupported action kind.\" };\n if (!step.summary.trim()) return { allowed: false, reason: \"A human-readable action summary is required.\" };\n if (targetactions.has(step.kind) && !step.target?.trim()) return { allowed: false, reason: \"A page target is required.\" };\n if (step.kind === \"select\" && !step.value?.trim()) return { allowed: false, reason: \"A reviewed option value is required.\" };\n if (step.kind === \"wait\") {\n try { waitduration(step); } catch { return { allowed: false, reason: \"Wait duration must be zero or a positive number of milliseconds.\" }; }\n }\n if (step.kind === \"navigate\") {\n if (!step.value) return { allowed: false, reason: \"A navigation URL is required.\" };\n try {\n if (new URL(step.value).origin !== origin) return { allowed: false, reason: \"Navigation must remain within the approved origin.\" };\n } catch {\n return { allowed: false, reason: \"Navigation URL is invalid.\" };\n }\n }\n return { allowed: true };\n}\n\n/** Shared session gate: a live, unpaused session that still matches the active tab. */\nfunction sessiongate(input: { session: agentsession | undefined; tabid: number; origin: string; now: number; action: string }): policyevaluation {\n if (!input.session || input.session.stoppedat) return { allowed: false, reason: \"No active browser session exists.\" };\n if (input.session.expiresat <= input.now) return { allowed: false, reason: \"The browser session has expired.\" };\n if (input.session.pausedat) return { allowed: false, reason: `The browser session is paused and cannot ${input.action}.` };\n if (input.session.tabid !== input.tabid || input.session.origin !== input.origin) return { allowed: false, reason: `The ${input.action} is outside the approved tab or origin.` };\n return { allowed: true };\n}\n\n/** Applies the consent gate immediately before an action reaches the page bridge. */\nexport function canexecute(input: { session: agentsession | undefined; plan: agentplan | undefined; step: toolstep; tabid: number; origin: string; now?: number }): policyevaluation {\n const now = input.now ?? Date.now();\n const gate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: \"execute an action\" });\n if (!gate.allowed) return gate;\n if (!input.plan || input.plan.state !== \"approved\") return { allowed: false, reason: \"The plan has not received explicit approval.\" };\n if (input.plan.expiresat <= now) return { allowed: false, reason: \"The approved plan has expired.\" };\n return validatestep(input.step, input.origin);\n}\n\n/** Allows a non-mutating, temporary target preview during plan review. */\nexport function canpreview(input: { session: agentsession | undefined; plan: agentplan | undefined; step: toolstep; tabid: number; origin: string; now?: number }): policyevaluation {\n const now = input.now ?? Date.now();\n const gate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: \"preview a target\" });\n if (!gate.allowed) return gate;\n if (!input.plan || ![\"pending\", \"approved\"].includes(input.plan.state)) return { allowed: false, reason: \"Only a reviewed pending or approved plan can be previewed.\" };\n if (input.plan.expiresat <= now) return { allowed: false, reason: \"The reviewed plan has expired.\" };\n if (![\"focus\", \"inspect\", \"click\", \"type\", \"scroll\", \"select\", \"hover\"].includes(input.step.kind)) return { allowed: false, reason: \"Only a target-based action can be previewed.\" };\n return validatestep(input.step, input.origin);\n}\n", "import { hostpattern, normalizeendpoint } from \"../policy.js\";\n\nconst endpointinput = document.querySelector<HTMLInputElement>(\"#endpoint\");\nconst statusnode = document.querySelector<HTMLElement>(\"#status\");\nconst connectbutton = document.querySelector<HTMLButtonElement>(\"#connect\");\nconst sessionbutton = document.querySelector<HTMLButtonElement>(\"#session\");\nconst pausebutton = document.querySelector<HTMLButtonElement>(\"#pause\");\nconst stopbutton = document.querySelector<HTMLButtonElement>(\"#stop\");\nconst openbutton = document.querySelector<HTMLButtonElement>(\"#openpanel\");\n\nfunction status(message: string, error = false): void { if (statusnode) { statusnode.textContent = message; statusnode.dataset.state = error ? \"error\" : \"ready\"; } }\nasync function request(message: unknown): Promise<unknown> { const response = await chrome.runtime.sendMessage(message) as { ok: boolean; value?: unknown; error?: string }; if (!response.ok) throw new Error(response.error); return response.value; }\n\nfunction pauselabel(paused: boolean): string { return paused ? \"Resume session\" : \"Pause session\"; }\n\nasync function restore(): Promise<void> {\n const context = await request({ kind: \"context\" }) as { config?: { endpoint: string }; session?: { stoppedat?: number; pausedat?: number; expiresat: number } };\n if (endpointinput && context.config) endpointinput.value = context.config.endpoint;\n const active = context.session && !context.session.stoppedat && context.session.expiresat > Date.now();\n if (pausebutton) { pausebutton.disabled = !active; pausebutton.textContent = pauselabel(Boolean(context.session?.pausedat)); }\n if (active && context.session?.pausedat) status(\"Session paused. Reviewed actions are blocked until resume.\");\n else status(active ? \"Session active. Review the plan in the side panel.\" : \"No active browser session.\");\n}\n\nconnectbutton?.addEventListener(\"click\", async () => {\n try {\n const config = normalizeendpoint(endpointinput?.value ?? \"\");\n const granted = await chrome.permissions.request({ origins: [hostpattern(config.origin)] });\n if (!granted) throw new Error(\"Origin permission was not granted.\");\n await request({ kind: \"configure\", endpoint: config.endpoint });\n status(`Endpoint approved for ${config.origin}.`);\n } catch (error) { status(error instanceof Error ? error.message : String(error), true); }\n});\nsessionbutton?.addEventListener(\"click\", async () => { try { await request({ kind: \"startsession\" }); status(\"Session started for the active HTTPS tab.\"); await restore(); } catch (error) { status(error instanceof Error ? error.message : String(error), true); } });\npausebutton?.addEventListener(\"click\", async () => { try { const context = await request({ kind: \"context\" }) as { session?: { pausedat?: number } }; await request({ kind: context.session?.pausedat ? \"resumesession\" : \"pausesession\" }); await restore(); } catch (error) { status(error instanceof Error ? error.message : String(error), true); } });\nstopbutton?.addEventListener(\"click\", async () => { try { await request({ kind: \"stop\" }); status(\"Session stopped. No action can continue.\"); await restore(); } catch (error) { status(error instanceof Error ? error.message : String(error), true); } });\nopenbutton?.addEventListener(\"click\", () => chrome.sidePanel.open({ windowId: chrome.windows.WINDOW_ID_CURRENT }));\nrestore().catch(error => status(error instanceof Error ? error.message : String(error), true));\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -26,7 +26,8 @@ function button(label, action, disabled = false) {
26
26
  element.addEventListener("click", () => action().catch((error) => status(error instanceof Error ? error.message : String(error), true)));
27
27
  return element;
28
28
  }
29
- function renderplan(plan) {
29
+ var previewkinds = ["focus", "inspect", "click", "type", "scroll", "select", "hover"];
30
+ function renderplan(plan, progress) {
30
31
  if (!planroot) return;
31
32
  planroot.replaceChildren();
32
33
  if (!plan) {
@@ -36,15 +37,17 @@ function renderplan(plan) {
36
37
  const title = document.createElement("h2");
37
38
  title.textContent = `${plan.state}: ${plan.objective}`;
38
39
  planroot.append(title);
40
+ const completed = progress?.planid === plan.id ? progress.completedsteps : [];
39
41
  const list = document.createElement("ol");
40
42
  for (const step of plan.steps) {
41
43
  const item = document.createElement("li");
42
- item.textContent = `${step.risk} \u2014 ${step.summary}`;
43
- if (step.target && ["focus", "inspect", "click", "type"].includes(step.kind) && ["pending", "approved"].includes(plan.state)) item.append(" ", button("Preview current target", async () => {
44
+ const done = completed.includes(step.id);
45
+ item.textContent = `${done ? "\u2713" : step.risk} \u2014 ${step.summary}`;
46
+ if (!done && step.target && previewkinds.includes(step.kind) && ["pending", "approved"].includes(plan.state)) item.append(" ", button("Preview current target", async () => {
44
47
  const result = await request({ kind: "preview", stepid: step.id });
45
48
  status(result.summary);
46
49
  }));
47
- if (plan.state === "approved") item.append(" ", button("Run this reviewed step", async () => {
50
+ if (!done && plan.state === "approved") item.append(" ", button("Run this reviewed step", async () => {
48
51
  const result = await request({ kind: "execute", stepid: step.id });
49
52
  status(result.summary);
50
53
  await refresh();
@@ -61,6 +64,11 @@ function renderplan(plan) {
61
64
  await refresh();
62
65
  }));
63
66
  }
67
+ if (plan.state === "completed" && plan.completedat) {
68
+ const note = document.createElement("p");
69
+ note.textContent = "Every reviewed step has executed and the plan is closed.";
70
+ planroot.append(note);
71
+ }
64
72
  }
65
73
  function renderaudit(events) {
66
74
  if (!auditroot) return;
@@ -87,10 +95,11 @@ function renderdiagnostic(report) {
87
95
  }
88
96
  async function refresh() {
89
97
  const context = await request({ kind: "context" });
90
- renderplan(context.plan);
98
+ renderplan(context.plan, context.progress);
91
99
  renderdiagnostic(context.diagnostic);
92
100
  renderaudit(context.audit);
93
- status(context.session ? "Active session is visible. The extension is waiting for review." : "No active browser session.");
101
+ if (context.session?.pausedat) status("Session paused. Reviewed actions are blocked until resume.");
102
+ else status(context.session ? "Active session is visible. The extension is waiting for review." : "No active browser session.");
94
103
  }
95
104
  async function create(kind) {
96
105
  await request({ kind, objective: objective?.value ?? "" });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../sidepanel.ts"],
4
- "sourcesContent": ["import type { agentplan, auditevent, diagnosticreport } from \"../types.js\";\n\nconst objective = document.querySelector<HTMLTextAreaElement>(\"#objective\");\nconst localbutton = document.querySelector<HTMLButtonElement>(\"#localplan\");\nconst remotebutton = document.querySelector<HTMLButtonElement>(\"#remoteplan\");\nconst diagnosticbutton = document.querySelector<HTMLButtonElement>(\"#diagnostic\");\nconst planroot = document.querySelector<HTMLElement>(\"#plan\");\nconst auditroot = document.querySelector<HTMLElement>(\"#audit\");\nconst diagnosticroot = document.querySelector<HTMLElement>(\"#diagnostics\");\nconst statusnode = document.querySelector<HTMLElement>(\"#status\");\n\nfunction status(message: string, error = false): void { if (statusnode) { statusnode.textContent = message; statusnode.dataset.state = error ? \"error\" : \"ready\"; } }\nasync function request(message: unknown): Promise<unknown> { const response = await chrome.runtime.sendMessage(message) as { ok: boolean; value?: unknown; error?: string }; if (!response.ok) throw new Error(response.error); return response.value; }\nfunction button(label: string, action: () => Promise<void>, disabled = false): HTMLButtonElement { const element = document.createElement(\"button\"); element.type = \"button\"; element.textContent = label; element.disabled = disabled; element.addEventListener(\"click\", () => action().catch(error => status(error instanceof Error ? error.message : String(error), true))); return element; }\nfunction renderplan(plan?: agentplan): void {\n if (!planroot) return;\n planroot.replaceChildren();\n if (!plan) { planroot.textContent = \"Start a session, then request a local or endpoint plan. No task runs before review.\"; return; }\n const title = document.createElement(\"h2\"); title.textContent = `${plan.state}: ${plan.objective}`; planroot.append(title);\n const list = document.createElement(\"ol\");\n for (const step of plan.steps) {\n const item = document.createElement(\"li\");\n item.textContent = `${step.risk} \u2014 ${step.summary}`;\n if (step.target && [\"focus\", \"inspect\", \"click\", \"type\"].includes(step.kind) && [\"pending\", \"approved\"].includes(plan.state)) item.append(\" \", button(\"Preview current target\", async () => { const result = await request({ kind: \"preview\", stepid: step.id }) as { summary: string }; status(result.summary); }));\n if (plan.state === \"approved\") item.append(\" \", button(\"Run this reviewed step\", async () => { const result = await request({ kind: \"execute\", stepid: step.id }) as { summary: string }; status(result.summary); await refresh(); }));\n list.append(item);\n }\n planroot.append(list);\n if (plan.state === \"pending\") { planroot.append(button(\"Approve reviewed plan\", async () => { await request({ kind: \"approve\" }); await refresh(); }), button(\"Reject plan\", async () => { await request({ kind: \"reject\" }); await refresh(); })); }\n}\nfunction renderaudit(events: auditevent[]): void { if (!auditroot) return; auditroot.replaceChildren(); for (const event of events.slice(0, 12)) { const item = document.createElement(\"li\"); item.textContent = `${new Date(event.at).toLocaleTimeString()} \u00B7 ${event.kind} \u00B7 ${event.summary}`; auditroot.append(item); } }\nfunction renderdiagnostic(report?: diagnosticreport): void { if (!diagnosticroot) return; diagnosticroot.replaceChildren(); if (!report) { diagnosticroot.textContent = \"Run a local diagnostic after starting a session to record bridge and page-shape health.\"; return; } const values = [`origin: ${report.origin}`, `title: ${report.title || \"untitled\"}`, `interactive elements: ${report.interactivecount}`, `forms: ${report.formcount}`, `page text length: ${report.textlength}`, `bridge available: ${report.bridgeavailable ? \"yes\" : \"no\"}`]; for (const value of values) { const item = document.createElement(\"li\"); item.textContent = value; diagnosticroot.append(item); } }\nasync function refresh(): Promise<void> { const context = await request({ kind: \"context\" }) as { plan?: agentplan; diagnostic?: diagnosticreport; audit: auditevent[]; session?: { id: string } }; renderplan(context.plan); renderdiagnostic(context.diagnostic); renderaudit(context.audit); status(context.session ? \"Active session is visible. The extension is waiting for review.\" : \"No active browser session.\"); }\nasync function create(kind: \"proposelocal\" | \"proposeremote\"): Promise<void> { await request({ kind, objective: objective?.value ?? \"\" }); await refresh(); }\nlocalbutton?.addEventListener(\"click\", () => create(\"proposelocal\").catch(error => status(error instanceof Error ? error.message : String(error), true)));\nremotebutton?.addEventListener(\"click\", () => create(\"proposeremote\").catch(error => status(error instanceof Error ? error.message : String(error), true)));\ndiagnosticbutton?.addEventListener(\"click\", () => request({ kind: \"diagnostic\" }).then(() => refresh()).catch(error => status(error instanceof Error ? error.message : String(error), true)));\nrefresh().catch(error => status(error instanceof Error ? error.message : String(error), true));\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { agentplan, auditevent, diagnosticreport, planprogress } from \"../types.js\";\n\nconst objective = document.querySelector<HTMLTextAreaElement>(\"#objective\");\nconst localbutton = document.querySelector<HTMLButtonElement>(\"#localplan\");\nconst remotebutton = document.querySelector<HTMLButtonElement>(\"#remoteplan\");\nconst diagnosticbutton = document.querySelector<HTMLButtonElement>(\"#diagnostic\");\nconst planroot = document.querySelector<HTMLElement>(\"#plan\");\nconst auditroot = document.querySelector<HTMLElement>(\"#audit\");\nconst diagnosticroot = document.querySelector<HTMLElement>(\"#diagnostics\");\nconst statusnode = document.querySelector<HTMLElement>(\"#status\");\n\nfunction status(message: string, error = false): void { if (statusnode) { statusnode.textContent = message; statusnode.dataset.state = error ? \"error\" : \"ready\"; } }\nasync function request(message: unknown): Promise<unknown> { const response = await chrome.runtime.sendMessage(message) as { ok: boolean; value?: unknown; error?: string }; if (!response.ok) throw new Error(response.error); return response.value; }\nfunction button(label: string, action: () => Promise<void>, disabled = false): HTMLButtonElement { const element = document.createElement(\"button\"); element.type = \"button\"; element.textContent = label; element.disabled = disabled; element.addEventListener(\"click\", () => action().catch(error => status(error instanceof Error ? error.message : String(error), true))); return element; }\nconst previewkinds = [\"focus\", \"inspect\", \"click\", \"type\", \"scroll\", \"select\", \"hover\"];\n\nfunction renderplan(plan?: agentplan, progress?: planprogress): void {\n if (!planroot) return;\n planroot.replaceChildren();\n if (!plan) { planroot.textContent = \"Start a session, then request a local or endpoint plan. No task runs before review.\"; return; }\n const title = document.createElement(\"h2\"); title.textContent = `${plan.state}: ${plan.objective}`; planroot.append(title);\n const completed = progress?.planid === plan.id ? progress.completedsteps : [];\n const list = document.createElement(\"ol\");\n for (const step of plan.steps) {\n const item = document.createElement(\"li\");\n const done = completed.includes(step.id);\n item.textContent = `${done ? \"\u2713\" : step.risk} \u2014 ${step.summary}`;\n if (!done && step.target && previewkinds.includes(step.kind) && [\"pending\", \"approved\"].includes(plan.state)) item.append(\" \", button(\"Preview current target\", async () => { const result = await request({ kind: \"preview\", stepid: step.id }) as { summary: string }; status(result.summary); }));\n if (!done && plan.state === \"approved\") item.append(\" \", button(\"Run this reviewed step\", async () => { const result = await request({ kind: \"execute\", stepid: step.id }) as { summary: string }; status(result.summary); await refresh(); }));\n list.append(item);\n }\n planroot.append(list);\n if (plan.state === \"pending\") { planroot.append(button(\"Approve reviewed plan\", async () => { await request({ kind: \"approve\" }); await refresh(); }), button(\"Reject plan\", async () => { await request({ kind: \"reject\" }); await refresh(); })); }\n if (plan.state === \"completed\" && plan.completedat) { const note = document.createElement(\"p\"); note.textContent = \"Every reviewed step has executed and the plan is closed.\"; planroot.append(note); }\n}\nfunction renderaudit(events: auditevent[]): void { if (!auditroot) return; auditroot.replaceChildren(); for (const event of events.slice(0, 12)) { const item = document.createElement(\"li\"); item.textContent = `${new Date(event.at).toLocaleTimeString()} \u00B7 ${event.kind} \u00B7 ${event.summary}`; auditroot.append(item); } }\nfunction renderdiagnostic(report?: diagnosticreport): void { if (!diagnosticroot) return; diagnosticroot.replaceChildren(); if (!report) { diagnosticroot.textContent = \"Run a local diagnostic after starting a session to record bridge and page-shape health.\"; return; } const values = [`origin: ${report.origin}`, `title: ${report.title || \"untitled\"}`, `interactive elements: ${report.interactivecount}`, `forms: ${report.formcount}`, `page text length: ${report.textlength}`, `bridge available: ${report.bridgeavailable ? \"yes\" : \"no\"}`]; for (const value of values) { const item = document.createElement(\"li\"); item.textContent = value; diagnosticroot.append(item); } }\nasync function refresh(): Promise<void> { const context = await request({ kind: \"context\" }) as { plan?: agentplan; progress?: planprogress; diagnostic?: diagnosticreport; audit: auditevent[]; session?: { id: string; pausedat?: number } }; renderplan(context.plan, context.progress); renderdiagnostic(context.diagnostic); renderaudit(context.audit); if (context.session?.pausedat) status(\"Session paused. Reviewed actions are blocked until resume.\"); else status(context.session ? \"Active session is visible. The extension is waiting for review.\" : \"No active browser session.\"); }\nasync function create(kind: \"proposelocal\" | \"proposeremote\"): Promise<void> { await request({ kind, objective: objective?.value ?? \"\" }); await refresh(); }\nlocalbutton?.addEventListener(\"click\", () => create(\"proposelocal\").catch(error => status(error instanceof Error ? error.message : String(error), true)));\nremotebutton?.addEventListener(\"click\", () => create(\"proposeremote\").catch(error => status(error instanceof Error ? error.message : String(error), true)));\ndiagnosticbutton?.addEventListener(\"click\", () => request({ kind: \"diagnostic\" }).then(() => refresh()).catch(error => status(error instanceof Error ? error.message : String(error), true)));\nrefresh().catch(error => status(error instanceof Error ? error.message : String(error), true));\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 3,
3
3
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnOEjO8Z0PDgQyfvawGcaO2j+o0GLCFTLNj7TkYC/Avo9l2NenMRq7gp90Nfd7E9MViv/OMcCKSYZ5unv12QPRtv31C+a5UQWDFAOP/cH5mwMd6hsayElrSoW8ta+FwFqmr9dIFkn7cQEU3YhZr4Gcbs+ycUHOxVgDA4NBKB0rQ6e9VW5LvTw0isRYUrqM+M72vKxHk9zUIYYn/LGPvottKBYi2GLr0PHSeC2UE+Shmq7vcFIXj6hDjvD4kLJ5sKoUllEcZ1TPuBcnHUQ9ndKA5iktXDQOIJCUJmi7a0YJ2PGg7fvpYfT9k0ai/qZ+pIoRfoOEwE01bPoDn7NjeYnNQIDAQAB",
4
4
  "name": "Devthink",
5
- "version": "1.1.30",
5
+ "version": "1.1.31",
6
6
  "description": "A consent-first bridge for reviewed browser-agent tasks.",
7
7
  "permissions": [
8
8
  "activeTab",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wenathlan/extension",
3
- "version": "1.1.30",
3
+ "version": "1.1.31",
4
4
  "description": "Consent-first browser agent bridge and Manifest V3 extension.",
5
5
  "type": "module",
6
6
  "license": "GPL-3.0-only",