@tinyviber/a2h 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/README.md +229 -0
  2. package/bin/a2h.js +7 -0
  3. package/dist/actions/engine.d.ts +40 -0
  4. package/dist/actions/engine.d.ts.map +1 -0
  5. package/dist/actions/engine.js +227 -0
  6. package/dist/actions/engine.js.map +1 -0
  7. package/dist/actions/policy.d.ts +8 -0
  8. package/dist/actions/policy.d.ts.map +1 -0
  9. package/dist/actions/policy.js +55 -0
  10. package/dist/actions/policy.js.map +1 -0
  11. package/dist/cli/index.d.ts +2 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +258 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/cli/init.d.ts +8 -0
  16. package/dist/cli/init.d.ts.map +1 -0
  17. package/dist/cli/init.js +69 -0
  18. package/dist/cli/init.js.map +1 -0
  19. package/dist/index.d.ts +26 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +66 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/ir/build.d.ts +8 -0
  24. package/dist/ir/build.d.ts.map +1 -0
  25. package/dist/ir/build.js +397 -0
  26. package/dist/ir/build.js.map +1 -0
  27. package/dist/ir/enrich.d.ts +21 -0
  28. package/dist/ir/enrich.d.ts.map +1 -0
  29. package/dist/ir/enrich.js +177 -0
  30. package/dist/ir/enrich.js.map +1 -0
  31. package/dist/ir/identity.d.ts +3 -0
  32. package/dist/ir/identity.d.ts.map +1 -0
  33. package/dist/ir/identity.js +91 -0
  34. package/dist/ir/identity.js.map +1 -0
  35. package/dist/ir/scoring.d.ts +7 -0
  36. package/dist/ir/scoring.d.ts.map +1 -0
  37. package/dist/ir/scoring.js +88 -0
  38. package/dist/ir/scoring.js.map +1 -0
  39. package/dist/parsers/code.d.ts +3 -0
  40. package/dist/parsers/code.d.ts.map +1 -0
  41. package/dist/parsers/code.js +17 -0
  42. package/dist/parsers/code.js.map +1 -0
  43. package/dist/parsers/diff.d.ts +11 -0
  44. package/dist/parsers/diff.d.ts.map +1 -0
  45. package/dist/parsers/diff.js +125 -0
  46. package/dist/parsers/diff.js.map +1 -0
  47. package/dist/parsers/json.d.ts +3 -0
  48. package/dist/parsers/json.d.ts.map +1 -0
  49. package/dist/parsers/json.js +16 -0
  50. package/dist/parsers/json.js.map +1 -0
  51. package/dist/parsers/log.d.ts +3 -0
  52. package/dist/parsers/log.d.ts.map +1 -0
  53. package/dist/parsers/log.js +105 -0
  54. package/dist/parsers/log.js.map +1 -0
  55. package/dist/parsers/markdown.d.ts +15 -0
  56. package/dist/parsers/markdown.d.ts.map +1 -0
  57. package/dist/parsers/markdown.js +89 -0
  58. package/dist/parsers/markdown.js.map +1 -0
  59. package/dist/parsers/text.d.ts +24 -0
  60. package/dist/parsers/text.d.ts.map +1 -0
  61. package/dist/parsers/text.js +98 -0
  62. package/dist/parsers/text.js.map +1 -0
  63. package/dist/presentation/blocks.d.ts +14 -0
  64. package/dist/presentation/blocks.d.ts.map +1 -0
  65. package/dist/presentation/blocks.js +87 -0
  66. package/dist/presentation/blocks.js.map +1 -0
  67. package/dist/presentation/present.d.ts +12 -0
  68. package/dist/presentation/present.d.ts.map +1 -0
  69. package/dist/presentation/present.js +115 -0
  70. package/dist/presentation/present.js.map +1 -0
  71. package/dist/providers/mock.d.ts +3 -0
  72. package/dist/providers/mock.d.ts.map +1 -0
  73. package/dist/providers/mock.js +171 -0
  74. package/dist/providers/mock.js.map +1 -0
  75. package/dist/providers/registry.d.ts +12 -0
  76. package/dist/providers/registry.d.ts.map +1 -0
  77. package/dist/providers/registry.js +20 -0
  78. package/dist/providers/registry.js.map +1 -0
  79. package/dist/providers/types.d.ts +64 -0
  80. package/dist/providers/types.d.ts.map +1 -0
  81. package/dist/providers/types.js +40 -0
  82. package/dist/providers/types.js.map +1 -0
  83. package/dist/renderer/assets/index.html +41 -0
  84. package/dist/renderer/assets/js/actions.js +205 -0
  85. package/dist/renderer/assets/js/api.js +75 -0
  86. package/dist/renderer/assets/js/artifacts.js +422 -0
  87. package/dist/renderer/assets/js/blocks.js +301 -0
  88. package/dist/renderer/assets/js/bus.js +21 -0
  89. package/dist/renderer/assets/js/cards.js +137 -0
  90. package/dist/renderer/assets/js/dom.js +61 -0
  91. package/dist/renderer/assets/js/format.js +118 -0
  92. package/dist/renderer/assets/js/main.js +125 -0
  93. package/dist/renderer/assets/js/nav.js +80 -0
  94. package/dist/renderer/assets/js/router.js +64 -0
  95. package/dist/renderer/assets/js/store.js +69 -0
  96. package/dist/renderer/assets/js/views.js +356 -0
  97. package/dist/renderer/assets/styles.css +856 -0
  98. package/dist/renderer/content.d.ts +11 -0
  99. package/dist/renderer/content.d.ts.map +1 -0
  100. package/dist/renderer/content.js +114 -0
  101. package/dist/renderer/content.js.map +1 -0
  102. package/dist/scanner/classify.d.ts +17 -0
  103. package/dist/scanner/classify.d.ts.map +1 -0
  104. package/dist/scanner/classify.js +117 -0
  105. package/dist/scanner/classify.js.map +1 -0
  106. package/dist/scanner/git.d.ts +3 -0
  107. package/dist/scanner/git.d.ts.map +1 -0
  108. package/dist/scanner/git.js +43 -0
  109. package/dist/scanner/git.js.map +1 -0
  110. package/dist/scanner/ignore.d.ts +22 -0
  111. package/dist/scanner/ignore.d.ts.map +1 -0
  112. package/dist/scanner/ignore.js +57 -0
  113. package/dist/scanner/ignore.js.map +1 -0
  114. package/dist/scanner/scan.d.ts +8 -0
  115. package/dist/scanner/scan.d.ts.map +1 -0
  116. package/dist/scanner/scan.js +108 -0
  117. package/dist/scanner/scan.js.map +1 -0
  118. package/dist/security/boundary.d.ts +19 -0
  119. package/dist/security/boundary.d.ts.map +1 -0
  120. package/dist/security/boundary.js +59 -0
  121. package/dist/security/boundary.js.map +1 -0
  122. package/dist/security/urlPolicy.d.ts +4 -0
  123. package/dist/security/urlPolicy.d.ts.map +1 -0
  124. package/dist/security/urlPolicy.js +56 -0
  125. package/dist/security/urlPolicy.js.map +1 -0
  126. package/dist/security/workspaceRead.d.ts +25 -0
  127. package/dist/security/workspaceRead.d.ts.map +1 -0
  128. package/dist/security/workspaceRead.js +103 -0
  129. package/dist/security/workspaceRead.js.map +1 -0
  130. package/dist/semantics/load.d.ts +11 -0
  131. package/dist/semantics/load.d.ts.map +1 -0
  132. package/dist/semantics/load.js +537 -0
  133. package/dist/semantics/load.js.map +1 -0
  134. package/dist/semantics/resolve.d.ts +37 -0
  135. package/dist/semantics/resolve.d.ts.map +1 -0
  136. package/dist/semantics/resolve.js +211 -0
  137. package/dist/semantics/resolve.js.map +1 -0
  138. package/dist/semantics/types.d.ts +158 -0
  139. package/dist/semantics/types.d.ts.map +1 -0
  140. package/dist/semantics/types.js +3 -0
  141. package/dist/semantics/types.js.map +1 -0
  142. package/dist/server/server.d.ts +17 -0
  143. package/dist/server/server.d.ts.map +1 -0
  144. package/dist/server/server.js +510 -0
  145. package/dist/server/server.js.map +1 -0
  146. package/dist/server/workspace.d.ts +53 -0
  147. package/dist/server/workspace.d.ts.map +1 -0
  148. package/dist/server/workspace.js +211 -0
  149. package/dist/server/workspace.js.map +1 -0
  150. package/dist/types.d.ts +570 -0
  151. package/dist/types.d.ts.map +1 -0
  152. package/dist/types.js +19 -0
  153. package/dist/types.js.map +1 -0
  154. package/dist/util/path.d.ts +11 -0
  155. package/dist/util/path.d.ts.map +1 -0
  156. package/dist/util/path.js +35 -0
  157. package/dist/util/path.js.map +1 -0
  158. package/dist/util/safeRead.d.ts +38 -0
  159. package/dist/util/safeRead.d.ts.map +1 -0
  160. package/dist/util/safeRead.js +117 -0
  161. package/dist/util/safeRead.js.map +1 -0
  162. package/package.json +40 -0
package/README.md ADDED
@@ -0,0 +1,229 @@
1
+ # A2H — Agent to Human
2
+
3
+ **Turn agent workspaces into human-readable, inspectable, actionable local web interfaces.**
4
+
5
+ Agents are good at producing files, logs, diffs, reports, intermediate state, and machine-friendly metadata. Humans are not good at supervising all of that through a terminal or by opening folders one file at a time.
6
+
7
+ A2H is the layer in between:
8
+
9
+ ```text
10
+ Agent workspace
11
+
12
+ scan + semantics
13
+
14
+ presentation
15
+
16
+ Human interface
17
+ ```
18
+
19
+ It works with an ordinary repository out of the box, and agents can optionally describe richer semantics through a small `.a2h/manifest.json` protocol.
20
+
21
+ A2H is **not** another agent runtime or workflow engine. It is the human-facing layer for reading, inspecting, comparing, judging, and acting on work produced by agents.
22
+
23
+ ## Quick start
24
+
25
+ Requires Node.js 18+.
26
+
27
+ ```bash
28
+ npx a2h render . --open
29
+ ```
30
+
31
+ A2H scans the current workspace and starts a local viewer on `http://localhost:8420`.
32
+
33
+ For live updates while an agent is working:
34
+
35
+ ```bash
36
+ npx a2h render . --watch --open
37
+ ```
38
+
39
+ No configuration is required.
40
+
41
+ ## Zero-config first
42
+
43
+ On an ordinary repository, A2H infers a useful presentation from the files already there:
44
+
45
+ - README and Markdown
46
+ - code
47
+ - diffs
48
+ - logs
49
+ - JSON / structured data
50
+ - images
51
+ - reports and generated artifacts
52
+
53
+ File names, directory conventions, content type, and lightweight heuristics are used only as fallbacks. Existing repositories do not need to adopt an A2H-specific structure.
54
+
55
+ ## Explicit semantics for agents
56
+
57
+ When a producer wants more control, it can write `.a2h/manifest.json` and tell A2H what the workspace means.
58
+
59
+ Start from the current workspace automatically:
60
+
61
+ ```bash
62
+ npx a2h init .
63
+ ```
64
+
65
+ A small manifest can look like this:
66
+
67
+ ```json
68
+ {
69
+ "a2h": 1,
70
+ "name": "Coding task",
71
+ "tasks": [
72
+ {
73
+ "id": "review",
74
+ "title": "Review implementation",
75
+ "status": "waiting-for-human",
76
+ "actions": ["approve"]
77
+ }
78
+ ],
79
+ "actions": [
80
+ {
81
+ "id": "approve",
82
+ "label": "Approve",
83
+ "kind": "approve",
84
+ "taskId": "review",
85
+ "sideEffect": "state"
86
+ }
87
+ ],
88
+ "items": [
89
+ {
90
+ "path": "review/summary.md",
91
+ "role": "review",
92
+ "taskId": "review",
93
+ "priority": 900
94
+ },
95
+ {
96
+ "path": "patches/change.diff",
97
+ "role": "implementation-diff",
98
+ "taskId": "review"
99
+ }
100
+ ]
101
+ }
102
+ ```
103
+
104
+ The important separation is:
105
+
106
+ ```text
107
+ role = what this artifact means
108
+ content kind = how its bytes should be rendered
109
+ ```
110
+
111
+ So a producer may invent roles such as `spec`, `evidence`, `decision`, `signal`, or `publish-candidate` without teaching the renderer new file types.
112
+
113
+ When signals conflict, A2H prefers explicit semantics and falls back progressively to conventions and inference.
114
+
115
+ ## Tasks, runs, presentation blocks, and actions
116
+
117
+ The protocol can describe more than files. A workspace can expose:
118
+
119
+ - tasks and their status
120
+ - agent / workflow runs
121
+ - artifacts associated with a task or run
122
+ - metrics, tables, timelines, comparisons, notices, and other presentation blocks
123
+ - human-facing actions such as approve, reject, retry, or send back to an agent
124
+
125
+ The built-in executor currently **simulates actions by default**. The UI says when an action is simulated. Real agent integrations are intended to sit behind provider / adapter boundaries rather than being hard-coded into A2H.
126
+
127
+ ## Examples
128
+
129
+ Two example workspaces are included in the repository.
130
+
131
+ ### Radar
132
+
133
+ A simulated daily agent workflow that ingests sources, clusters signals, writes publish candidates, and stops for human review.
134
+
135
+ ```bash
136
+ npx a2h render examples/radar --open
137
+ ```
138
+
139
+ See [`examples/radar`](examples/radar) and its [manifest](examples/radar/.a2h/manifest.json).
140
+
141
+ ### Coding task
142
+
143
+ A workspace centered on a coding-agent task with a spec, implementation, tests, patch, screenshots, and review decisions.
144
+
145
+ ```bash
146
+ npx a2h render examples/coding-task --open
147
+ ```
148
+
149
+ See [`examples/coding-task`](examples/coding-task).
150
+
151
+ ## CLI
152
+
153
+ ```text
154
+ a2h [render] [path] [options]
155
+ a2h init [path]
156
+ ```
157
+
158
+ Common options:
159
+
160
+ ```text
161
+ -w, --watch Watch the workspace and refresh on changes
162
+ -p, --port <n> Viewer port (default: 8420)
163
+ -o, --open Open the viewer in the default browser
164
+ -f, --force Overwrite an existing manifest when using init
165
+ -v, --version Print the version
166
+ ```
167
+
168
+ Examples:
169
+
170
+ ```bash
171
+ npx a2h render
172
+ npx a2h render ../agent-workspace --watch
173
+ npx a2h init .
174
+ npx a2h render . --port 9000 --open
175
+ ```
176
+
177
+ ## Design principles
178
+
179
+ A2H is deliberately local-first and content-first.
180
+
181
+ The interface is designed for humans to:
182
+
183
+ **read → inspect → compare → judge → act when necessary**
184
+
185
+ The semantic layer is designed for machines to emit structured meaning without coupling themselves to a specific frontend.
186
+
187
+ The zero-config path remains important: explicit metadata should improve a workspace, not become a requirement for using A2H.
188
+
189
+ ## Security model
190
+
191
+ A2H treats the workspace as untrusted input.
192
+
193
+ Workspace file reads go through a bounded capability that checks the scanner allowlist, sensitive-file classification, symlinks, and real-path containment. Raw workspace files are not exposed as arbitrary same-origin documents.
194
+
195
+ Mutating browser requests go through a separate control-surface boundary. Actions must be declared, browser parameters are filtered against the declared schema, external effects require confirmation, and localhost mutations require a per-process session token.
196
+
197
+ The server binds to `127.0.0.1` by default.
198
+
199
+ ## Development
200
+
201
+ ```bash
202
+ npm install
203
+ npm run build
204
+ npm test
205
+ npm run test:acceptance
206
+ ```
207
+
208
+ Run the CLI from the repository:
209
+
210
+ ```bash
211
+ node bin/a2h.js render examples/radar --open
212
+ ```
213
+
214
+ Before publishing:
215
+
216
+ ```bash
217
+ npm pack
218
+ npm publish --dry-run
219
+ ```
220
+
221
+ ## Status
222
+
223
+ A2H is early-stage. The protocol and presentation model are intentionally small and extensible, and real external agent providers are still evolving.
224
+
225
+ If you are experimenting with coding agents, scheduled agents, research pipelines, or any workflow where machines produce more state than a human wants to inspect manually, A2H is meant to be a thin human interface over that work.
226
+
227
+ ## License
228
+
229
+ MIT
package/bin/a2h.js ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // Thin entry point: loads the compiled CLI and hands off control.
5
+ // Kept as plain JS so the shebang survives publishing regardless of
6
+ // how the TypeScript build emits its output.
7
+ require('../dist/cli/index.js').main(process.argv.slice(2));
@@ -0,0 +1,40 @@
1
+ import type { ActionAuditEntry, ActionResult, ActionRequest, ActionView, RunView } from '../types';
2
+ import type { ProviderRegistry } from '../providers/types';
3
+ export type AuditEntry = ActionAuditEntry;
4
+ export interface ActionEngineOptions {
5
+ rootDir: string;
6
+ registry: ProviderRegistry;
7
+ now?: () => Date;
8
+ }
9
+ export declare class ActionEngine {
10
+ private readonly rootDir;
11
+ private readonly registry;
12
+ private readonly now;
13
+ private runtimeRuns;
14
+ private taskStatus;
15
+ private audit;
16
+ private seq;
17
+ constructor(options: ActionEngineOptions);
18
+ /** Runs produced by actions during this session, newest first. */
19
+ getRuntimeRuns(): RunView[];
20
+ /** Task status overrides applied by executed actions. */
21
+ getTaskStatus(taskId: string): string | undefined;
22
+ getAuditTrail(): AuditEntry[];
23
+ /**
24
+ * Executes a declared action.
25
+ *
26
+ * @param declared the action registry as published to the client. Anything
27
+ * not present here is rejected — this is the allowlist.
28
+ */
29
+ execute(request: ActionRequest, declared: ActionView[]): Promise<ActionResult>;
30
+ /**
31
+ * Records a refused or failed attempt and returns the result.
32
+ *
33
+ * Exactly one audit line is written per attempt — including the ones that
34
+ * never reached an executor. Attribution matters here: the trail is the
35
+ * record of what was asked for and what actually happened.
36
+ */
37
+ private fail;
38
+ private record;
39
+ }
40
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/actions/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAEhB,YAAY,EACZ,aAAa,EACb,UAAU,EACV,OAAO,EACR,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAkC3D,MAAM,MAAM,UAAU,GAAG,gBAAgB,CAAC;AAE1C,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAa;IAEjC,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,GAAG,CAAK;gBAEJ,OAAO,EAAE,mBAAmB;IAMxC,kEAAkE;IAClE,cAAc,IAAI,OAAO,EAAE;IAI3B,yDAAyD;IACzD,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIjD,aAAa,IAAI,UAAU,EAAE;IAI7B;;;;;OAKG;IACG,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC;IAuHpF;;;;;;OAMG;IACH,OAAO,CAAC,IAAI;IAoBZ,OAAO,CAAC,MAAM;CAIf"}
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ActionEngine = void 0;
4
+ const policy_1 = require("./policy");
5
+ // ---------------------------------------------------------------------------
6
+ // Action engine — the security boundary between the browser and anything with
7
+ // an effect.
8
+ //
9
+ // Guarantees, in order of importance:
10
+ //
11
+ // 1. Only actions *declared in the workspace manifest* can be executed. The
12
+ // browser sends an id; it can never send a command.
13
+ // 2. Values from the browser are filtered against the declared schema before
14
+ // an executor ever sees them: undeclared keys are dropped, and a value
15
+ // that is not legal for its declared type is refused. Nothing is
16
+ // interpolated into a shell or used as a path.
17
+ // 3. An effect that reaches beyond A2H's own state requires an explicit
18
+ // second confirmation from the human. This is decided here, from two
19
+ // claims — the workspace's and the executor's — merged with the stricter
20
+ // winning. Neither is trusted on its own: the manifest is untrusted
21
+ // input, and it is the executor's declared policy that a workspace cannot
22
+ // talk down.
23
+ // 4. Execution is delegated to a registered executor. The built-in executor
24
+ // only simulates state changes, so nothing dangerous can happen by
25
+ // default — but the protocol and the UI flow are fully exercised.
26
+ //
27
+ // The engine keeps an in-memory audit trail and in-memory run/status state. It
28
+ // deliberately writes nothing to the workspace.
29
+ // ---------------------------------------------------------------------------
30
+ const MAX_PARAM_LENGTH = 2000;
31
+ const MAX_PARAMS = 20;
32
+ const MAX_RUNTIME_RUNS = 200;
33
+ const MAX_AUDIT = 200;
34
+ class ActionEngine {
35
+ rootDir;
36
+ registry;
37
+ now;
38
+ runtimeRuns = [];
39
+ taskStatus = new Map();
40
+ audit = [];
41
+ seq = 0;
42
+ constructor(options) {
43
+ this.rootDir = options.rootDir;
44
+ this.registry = options.registry;
45
+ this.now = options.now ?? (() => new Date());
46
+ }
47
+ /** Runs produced by actions during this session, newest first. */
48
+ getRuntimeRuns() {
49
+ return this.runtimeRuns;
50
+ }
51
+ /** Task status overrides applied by executed actions. */
52
+ getTaskStatus(taskId) {
53
+ return this.taskStatus.get(taskId);
54
+ }
55
+ getAuditTrail() {
56
+ return this.audit;
57
+ }
58
+ /**
59
+ * Executes a declared action.
60
+ *
61
+ * @param declared the action registry as published to the client. Anything
62
+ * not present here is rejected — this is the allowlist.
63
+ */
64
+ async execute(request, declared) {
65
+ const action = declared.find((a) => a.id === request.id);
66
+ if (!action) {
67
+ return this.fail(request.id, 'unknown_action', 'That action is not declared by this workspace.');
68
+ }
69
+ if (!action.enabled) {
70
+ return this.fail(action.id, 'disabled', `"${action.label}" is currently disabled.`);
71
+ }
72
+ const declaredParams = action.params ?? [];
73
+ const outcome = buildParams(request.params, declaredParams);
74
+ if (outcome.invalid) {
75
+ return this.fail(action.id, 'invalid_params', `"${outcome.invalid.name}" ${outcome.invalid.reason}.`);
76
+ }
77
+ const params = outcome.params;
78
+ const missing = declaredParams
79
+ .filter((p) => p.required && !String(params[p.name] ?? '').trim())
80
+ .map((p) => p.label ?? p.name);
81
+ if (missing.length > 0) {
82
+ return this.fail(action.id, 'missing_params', `Missing required input: ${missing.join(', ')}.`);
83
+ }
84
+ // Who runs this, and what they say it will do. Asked before the
85
+ // confirmation check, because the answer is half of that decision.
86
+ const resolution = this.registry.resolve(action);
87
+ if (!resolution) {
88
+ return this.fail(action.id, 'no_executor', `No provider can execute "${action.kind}" actions in this workspace.`);
89
+ }
90
+ // The hard boundary, recomputed from both claims rather than read off the
91
+ // incoming view: the workspace may escalate the level, the executor may
92
+ // escalate it, and the merge takes the higher one. A forged ActionView
93
+ // therefore cannot de-escalate anything either.
94
+ const policy = (0, policy_1.strictestPolicy)((0, policy_1.policyFromHint)(action.sideEffect, action.confirm), resolution.policy);
95
+ if (policy.confirmation === 'required' && request.confirm !== true) {
96
+ return this.fail(action.id, 'confirmation_required', `"${action.label}" has a ${policy.effect} side effect and needs confirmation.`, resolution.executor.id, policy.effect);
97
+ }
98
+ this.seq += 1;
99
+ const runId = `run-${this.seq}-${slug(action.kind)}`;
100
+ try {
101
+ const outcome = await resolution.executor.execute(action, params, {
102
+ workspaceRoot: this.rootDir,
103
+ runId,
104
+ now: this.now(),
105
+ });
106
+ // Provenance is the registry's answer OR the executor's, never the
107
+ // executor's alone: a simulator that forgets to label its output, or a
108
+ // provider that claims a real effect it did not have, both end up
109
+ // reported as simulated rather than the reverse.
110
+ const simulated = resolution.simulated || outcome.simulated === true;
111
+ const run = outcome.run
112
+ ? {
113
+ ...outcome.run,
114
+ id: outcome.run.id ?? runId,
115
+ taskId: action.taskId,
116
+ simulated,
117
+ }
118
+ : undefined;
119
+ if (run) {
120
+ this.runtimeRuns.unshift(run);
121
+ if (this.runtimeRuns.length > MAX_RUNTIME_RUNS) {
122
+ this.runtimeRuns.length = MAX_RUNTIME_RUNS;
123
+ }
124
+ }
125
+ if (action.taskId && outcome.taskStatus) {
126
+ this.taskStatus.set(action.taskId, outcome.taskStatus);
127
+ }
128
+ this.record({
129
+ at: this.now().toISOString(),
130
+ actionId: action.id,
131
+ kind: action.kind,
132
+ sideEffect: policy.effect,
133
+ ok: true,
134
+ simulated,
135
+ message: outcome.message,
136
+ executor: resolution.executor.id,
137
+ });
138
+ return {
139
+ ok: true,
140
+ actionId: action.id,
141
+ message: outcome.message,
142
+ simulated,
143
+ run,
144
+ task: action.taskId && outcome.taskStatus
145
+ ? { id: action.taskId, status: outcome.taskStatus }
146
+ : undefined,
147
+ };
148
+ }
149
+ catch (err) {
150
+ const message = `Executor "${resolution.executor.id}" failed: ${err.message}`;
151
+ return this.fail(action.id, 'executor_failed', message, resolution.executor.id, policy.effect);
152
+ }
153
+ }
154
+ /**
155
+ * Records a refused or failed attempt and returns the result.
156
+ *
157
+ * Exactly one audit line is written per attempt — including the ones that
158
+ * never reached an executor. Attribution matters here: the trail is the
159
+ * record of what was asked for and what actually happened.
160
+ */
161
+ fail(actionId, error, message, executor = 'none', sideEffect = 'none') {
162
+ this.record({
163
+ at: this.now().toISOString(),
164
+ actionId,
165
+ kind: 'n/a',
166
+ sideEffect,
167
+ ok: false,
168
+ simulated: false,
169
+ message,
170
+ executor,
171
+ });
172
+ return { ok: false, actionId, message, simulated: false, error };
173
+ }
174
+ record(entry) {
175
+ this.audit.unshift(entry);
176
+ if (this.audit.length > MAX_AUDIT)
177
+ this.audit.length = MAX_AUDIT;
178
+ }
179
+ }
180
+ exports.ActionEngine = ActionEngine;
181
+ /**
182
+ * Untrusted browser input, filtered against the action's declared schema.
183
+ *
184
+ * The distinguishing property is what is *absent* from the result: a key the
185
+ * action never declared. An executor written against the spec therefore cannot
186
+ * be reached by `shell`, `cwd`, or anything else a hostile page invents — not
187
+ * because the executor remembers to ignore them, but because they never arrive.
188
+ *
189
+ * Values that are legal for their type are normalized (`boolean` becomes the
190
+ * string "true"/"false"), so an executor has one shape to handle.
191
+ */
192
+ function buildParams(input, declared) {
193
+ const params = {};
194
+ if (typeof input !== 'object' || input === null || Array.isArray(input))
195
+ return { params };
196
+ const supplied = input;
197
+ for (const spec of declared.slice(0, MAX_PARAMS)) {
198
+ const raw = supplied[spec.name];
199
+ if (raw === undefined)
200
+ continue;
201
+ if (typeof raw !== 'string' && typeof raw !== 'number' && typeof raw !== 'boolean')
202
+ continue;
203
+ const value = String(raw).slice(0, MAX_PARAM_LENGTH);
204
+ if (spec.type === 'select') {
205
+ const options = spec.options ?? [];
206
+ // An out-of-range select is refused rather than passed through: a
207
+ // provider that branches on this value would otherwise be steerable.
208
+ if (options.length > 0 && !options.includes(value)) {
209
+ return { params, invalid: { name: spec.name, reason: `must be one of: ${options.join(', ')}` } };
210
+ }
211
+ }
212
+ if (spec.type === 'boolean') {
213
+ const normalized = value.trim().toLowerCase();
214
+ if (normalized !== 'true' && normalized !== 'false') {
215
+ return { params, invalid: { name: spec.name, reason: 'must be true or false' } };
216
+ }
217
+ params[spec.name] = normalized;
218
+ continue;
219
+ }
220
+ params[spec.name] = value;
221
+ }
222
+ return { params };
223
+ }
224
+ function slug(kind) {
225
+ return kind.replace(/[^a-z0-9]+/gi, '-').replace(/^-+|-+$/g, '').toLowerCase() || 'action';
226
+ }
227
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/actions/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { EffectPolicy, SideEffect } from '../types';
2
+ /** The policy a workspace's own declaration amounts to. */
3
+ export declare function policyFromHint(sideEffect?: SideEffect, confirm?: boolean): EffectPolicy;
4
+ /** The stricter of two policies: the higher effect, and confirmation if either asks. */
5
+ export declare function stricterPolicy(a: EffectPolicy, b: EffectPolicy): EffectPolicy;
6
+ /** Folds a list of claims into the policy that governs the action. */
7
+ export declare function strictestPolicy(...policies: EffectPolicy[]): EffectPolicy;
8
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/actions/policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAoCzD,2DAA2D;AAC3D,wBAAgB,cAAc,CAAC,UAAU,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,YAAY,CAKvF;AAED,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,YAAY,CAM7E;AAED,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,GAAG,QAAQ,EAAE,YAAY,EAAE,GAAG,YAAY,CAEzE"}
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.policyFromHint = policyFromHint;
4
+ exports.stricterPolicy = stricterPolicy;
5
+ exports.strictestPolicy = strictestPolicy;
6
+ // ---------------------------------------------------------------------------
7
+ // Effect policy — the one place the confirmation boundary is computed.
8
+ //
9
+ // There are two statements about how dangerous an action is:
10
+ //
11
+ // * the workspace's claim — `sideEffect` + `confirm` in the manifest. The
12
+ // workspace is untrusted input, so this is a *hint*.
13
+ // * the provider's claim — `ActionExecutor.policy()`. The provider is the
14
+ // code that will actually run, so this is *authoritative*.
15
+ //
16
+ // They are merged, and the stricter one wins, every time. A workspace can say
17
+ // "this is worse than you think" and get a confirmation prompt; it can never
18
+ // say "this is fine" and remove one. The old model let it: a manifest that
19
+ // called a future `publish` action `sideEffect: "state"` decided its own
20
+ // safety level, which is the wrong direction for untrusted input to point.
21
+ //
22
+ // The merge also normalizes one invariant: an effect that leaves this machine
23
+ // always requires confirmation, whatever either side said. Spelling it here
24
+ // rather than at the call site means there is no path that forgets it.
25
+ // ---------------------------------------------------------------------------
26
+ const RANK = { none: 0, state: 1, external: 2 };
27
+ /**
28
+ * The invariant, applied to every policy that leaves this module: an effect
29
+ * that reaches outside this machine is always confirmed. Stated once here
30
+ * rather than at each call site, because there is no path that should skip it.
31
+ */
32
+ function normalize(policy) {
33
+ return policy.effect === 'external' && policy.confirmation !== 'required'
34
+ ? { effect: 'external', confirmation: 'required' }
35
+ : policy;
36
+ }
37
+ /** The policy a workspace's own declaration amounts to. */
38
+ function policyFromHint(sideEffect, confirm) {
39
+ return normalize({
40
+ effect: sideEffect ?? 'state',
41
+ confirmation: confirm ? 'required' : 'optional',
42
+ });
43
+ }
44
+ /** The stricter of two policies: the higher effect, and confirmation if either asks. */
45
+ function stricterPolicy(a, b) {
46
+ return normalize({
47
+ effect: RANK[a.effect] >= RANK[b.effect] ? a.effect : b.effect,
48
+ confirmation: a.confirmation === 'required' || b.confirmation === 'required' ? 'required' : 'optional',
49
+ });
50
+ }
51
+ /** Folds a list of claims into the policy that governs the action. */
52
+ function strictestPolicy(...policies) {
53
+ return policies.reduce(stricterPolicy, { effect: 'none', confirmation: 'optional' });
54
+ }
55
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/actions/policy.ts"],"names":[],"mappings":";;AAqCA,wCAKC;AAGD,wCAMC;AAGD,0CAEC;AAtDD,8EAA8E;AAC9E,uEAAuE;AACvE,EAAE;AACF,6DAA6D;AAC7D,EAAE;AACF,6EAA6E;AAC7E,yDAAyD;AACzD,8EAA8E;AAC9E,+DAA+D;AAC/D,EAAE;AACF,8EAA8E;AAC9E,6EAA6E;AAC7E,2EAA2E;AAC3E,yEAAyE;AACzE,2EAA2E;AAC3E,EAAE;AACF,8EAA8E;AAC9E,4EAA4E;AAC5E,uEAAuE;AACvE,8EAA8E;AAE9E,MAAM,IAAI,GAA+B,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;AAE5E;;;;GAIG;AACH,SAAS,SAAS,CAAC,MAAoB;IACrC,OAAO,MAAM,CAAC,MAAM,KAAK,UAAU,IAAI,MAAM,CAAC,YAAY,KAAK,UAAU;QACvE,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE;QAClD,CAAC,CAAC,MAAM,CAAC;AACb,CAAC;AAED,2DAA2D;AAC3D,SAAgB,cAAc,CAAC,UAAuB,EAAE,OAAiB;IACvE,OAAO,SAAS,CAAC;QACf,MAAM,EAAE,UAAU,IAAI,OAAO;QAC7B,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU;KAChD,CAAC,CAAC;AACL,CAAC;AAED,wFAAwF;AACxF,SAAgB,cAAc,CAAC,CAAe,EAAE,CAAe;IAC7D,OAAO,SAAS,CAAC;QACf,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM;QAC9D,YAAY,EACV,CAAC,CAAC,YAAY,KAAK,UAAU,IAAI,CAAC,CAAC,YAAY,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU;KAC3F,CAAC,CAAC;AACL,CAAC;AAED,sEAAsE;AACtE,SAAgB,eAAe,CAAC,GAAG,QAAwB;IACzD,OAAO,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC;AACvF,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function main(argv: string[]): void;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAsBA,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAoBzC"}