@intentic/extension-manifest 1.248.0 → 1.250.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 (45) hide show
  1. package/dist/bundle.d.ts.map +1 -1
  2. package/dist/bundle.js.map +1 -1
  3. package/dist/contribution-point.d.ts.map +1 -1
  4. package/dist/json-schema.d.ts.map +1 -1
  5. package/dist/json-schema.js.map +1 -1
  6. package/dist/manifest.d.ts.map +1 -1
  7. package/dist/manifest.js.map +1 -1
  8. package/dist/mark.d.ts.map +1 -1
  9. package/dist/mark.js.map +1 -1
  10. package/dist/points/automation-templates.d.ts.map +1 -1
  11. package/dist/points/automation-templates.js.map +1 -1
  12. package/dist/points/capabilities.d.ts.map +1 -1
  13. package/dist/points/capabilities.js.map +1 -1
  14. package/dist/points/commands.d.ts.map +1 -1
  15. package/dist/points/commands.js.map +1 -1
  16. package/dist/points/documents.d.ts.map +1 -1
  17. package/dist/points/documents.js.map +1 -1
  18. package/dist/points/environment.d.ts.map +1 -1
  19. package/dist/points/environment.js.map +1 -1
  20. package/dist/points/files.d.ts.map +1 -1
  21. package/dist/points/files.js.map +1 -1
  22. package/dist/points/index.d.ts.map +1 -1
  23. package/dist/points/index.js.map +1 -1
  24. package/dist/points/listener.d.ts.map +1 -1
  25. package/dist/points/listener.js.map +1 -1
  26. package/dist/points/viewers.d.ts.map +1 -1
  27. package/dist/points/viewers.js.map +1 -1
  28. package/dist/powers-diff.d.ts.map +1 -1
  29. package/dist/powers-diff.js.map +1 -1
  30. package/package.json +3 -3
  31. package/src/bundle.ts +7 -17
  32. package/src/contribution-point.ts +6 -18
  33. package/src/json-schema.ts +9 -24
  34. package/src/manifest.ts +16 -45
  35. package/src/mark.ts +8 -44
  36. package/src/points/automation-templates.ts +12 -25
  37. package/src/points/capabilities.ts +43 -137
  38. package/src/points/commands.ts +4 -12
  39. package/src/points/documents.ts +3 -10
  40. package/src/points/environment.ts +4 -11
  41. package/src/points/files.ts +7 -26
  42. package/src/points/index.ts +7 -20
  43. package/src/points/listener.ts +11 -23
  44. package/src/points/viewers.ts +9 -13
  45. package/src/powers-diff.ts +7 -14
@@ -1,25 +1,12 @@
1
1
  import { z } from "zod";
2
2
  import type { ContributionPoint } from "../contribution-point.js";
3
3
 
4
- /* WHICH WORKSPACE FILE MAKES THIS EXTENSION'S VIEW STALE, the extension's half of the core's
5
- * WORKSPACE_STATE_FILES table (@intentic/sandbox-contract), in the same two fields so the browser can union them
6
- * without translating.
7
- *
8
- * An intentic workspace is file-first: the agent edits /work with its own file tools, out of band from every
9
- * HTTP route, and the daemon's filesystem watcher is the ONLY thing that can tell a browser its view went stale.
10
- * Before this contribution point existed an extension had no way into that push, so every one of them polled,
11
- * and the core's table had to hardcode `automations`/`automation-approvals`, query keys owned by an extension,
12
- * because the extension itself couldn't declare them. Declaring is now the extension's job and unioning is the
13
- * host's.
14
- *
15
- * It rides the manifest rather than a runtime api.workspace.onDidChangeFiles for two reasons: the owner sees at
16
- * install which of their files an extension reads, and there is nothing imperative left to get wrong, no
17
- * subscribe, no unsubscribe, no listener that quietly stops firing. */
4
+ // Which workspace file makes this extension's view stale; the extension's half of the core's WORKSPACE_STATE_FILES
5
+ // table (@intentic/sandbox-contract), same two fields so the browser can union them without translating. Rides the
6
+ // manifest rather than a runtime subscription, so the owner sees at install which files an extension reads.
18
7
  export const FileContributionSchema = z.object({
19
- /* Workspace-root-relative, forward-slash, the space the watcher's changed paths arrive in. Matched by
20
- * PREFIX, so one entry covers an exact file (`.intentic/config/automations.json`), a directory (`.intentic/config/approvals/`
21
- *, keep the trailing slash so it cannot match a sibling file) or a name family (`.intentic/environment.`).
22
- * Deliberately not a glob: prefix is the whole matching rule on both sides of this union. */
8
+ // Workspace-root-relative, forward-slash, matched by prefix: covers an exact file, a directory (keep the trailing
9
+ // slash), or a name family. Not a glob.
23
10
  path: z
24
11
  .string()
25
12
  .min(1)
@@ -29,14 +16,8 @@ export const FileContributionSchema = z.object({
29
16
  .describe(
30
17
  "Workspace-root-relative, forward-slash, matched by prefix, so one entry covers an exact file (`.intentic/config/automations.json`), a directory (`.intentic/config/approvals/`, with the trailing slash so it cannot match a sibling file) or a name family (`.intentic/environment.`). Not a glob.",
31
18
  ),
32
- /* The browser query keys those contents feed, the first element of the extension's own
33
- * `api.sandbox.key(...)` keys, which is what makes them match (the sandbox id is a SUFFIX). Empty is not
34
- * allowed: a path that makes nothing stale is a declaration with no effect, and saying so at install beats
35
- * discovering it as a view that never refreshes.
36
- *
37
- * Keep the paths as narrow as the view actually needs. A broad prefix costs every connected browser a
38
- * refetch per matching write, and a write-heavy path (an index, a transcript, a log) turns that into a
39
- * request storm, the reason the core table leaves the daemon's own machine state off the push entirely. */
19
+ // The browser query keys this path's contents feed. Keep both this and the path as narrow as the view actually
20
+ // needs; a broad prefix costs every connected browser a refetch per matching write.
40
21
  invalidates: z
41
22
  .array(z.string().min(1))
42
23
  .min(1)
@@ -28,17 +28,9 @@ export * from "./settings.js";
28
28
  export * from "./viewers.js";
29
29
  export * from "./views.js";
30
30
 
31
- /* EVERYTHING A MANIFEST MAY DECLARE. `contributes` is assembled from this list (manifest.ts), the authoring
32
- * JSON Schema is generated from it (json-schema.ts), and the SDK's surface guard reads the point names back out
33
- * of it, so the three cannot disagree about what this build supports.
34
- *
35
- * Collected explicitly rather than by a module-load side effect, because two readers need the answer to be the
36
- * same every time it is asked: the wire contract's lock file, which is a committed document a diff has to be
37
- * able to guard, and the generated schema, which is committed too. A registry that filled itself as modules
38
- * happened to load would make both of those depend on import order.
39
- *
40
- * Adding a point is a file in this directory and a line here, points.test.ts fails when a file appears without
41
- * the line, so the pair cannot come apart. */
31
+ // Everything a manifest may declare. `contributes` (manifest.ts), the authoring schema (json-schema.ts) and the SDK's
32
+ // surface guard are all generated from this list, so the three can't disagree about what this build supports. Adding a
33
+ // point is a file plus a line here; points.test.ts fails if they come apart.
42
34
  export const CONTRIBUTION_POINTS = [
43
35
  viewsPoint,
44
36
  filesPoint,
@@ -55,19 +47,14 @@ export const CONTRIBUTION_POINTS = [
55
47
  binPoint,
56
48
  ] as const satisfies readonly ContributionPoint[];
57
49
 
58
- // The `contributes` shape those points assemble to: each point's key, its schema, optional. A mapped type
59
- // rather than a widened record so `manifest.contributes.views` keeps its exact type at every call site, the
60
- // whole point of the schema being typed at all.
50
+ // The `contributes` shape those points assemble to. A mapped type rather than a widened record, so
51
+ // `manifest.contributes.views` keeps its exact type at every call site.
61
52
  type ContributesShape = {
62
53
  [Point in (typeof CONTRIBUTION_POINTS)[number] as Point["name"]]: z.ZodOptional<Point["schema"]>;
63
54
  };
64
55
 
65
- /* The `contributes` object, assembled rather than hand-written, which is what makes adding a point a file plus
66
- * a line above, instead of an edit to a schema thirteen unrelated features share.
67
- *
68
- * Each point's description rides `z.describe` onto its own key, so it survives into the generated authoring
69
- * schema and reaches the author as hover text. The cast is the one place the value side and the type side meet:
70
- * `Object.fromEntries` can only say `Record<string, …>`, and ContributesShape is that said precisely. */
56
+ // The `contributes` object, assembled rather than hand-written, so adding a point is a file plus a line above. Each
57
+ // point's description rides `z.describe` onto its own key, reaching the generated authoring schema as hover text.
71
58
  export const contributesSchema = z.object(
72
59
  Object.fromEntries(CONTRIBUTION_POINTS.map((point) => [point.name, point.schema.describe(point.description).optional()])) as ContributesShape,
73
60
  );
@@ -1,30 +1,19 @@
1
1
  import { z } from "zod";
2
2
  import type { ContributionPoint } from "../contribution-point.js";
3
3
 
4
- // One narrowing field the generic automation editor draws for a source, a channel, a branch. `hint` is the
5
- // sentence under the input, for a filter whose empty case is easy to get wrong.
4
+ // One narrowing field the generic automation editor draws for a source (a channel, a branch).
6
5
  const TriggerFieldContributionSchema = z.object({
7
6
  label: z.string().min(1),
8
7
  placeholder: z.string().min(1),
9
8
  hint: z.string().min(1).optional().describe("The sentence under the input, for a filter whose empty case is easy to get wrong."),
10
9
  });
11
10
 
12
- /* A realtime listener source the extension supplies. This is the ONE catalog both halves consume: the daemon
13
- * derives its accepted event types from `events` and folds `automation` into the trigger catalogue it serves,
14
- * while the automation editor derives the source picker, filters and starter from that. Keeping those facts on
15
- * the provider extension is what lets a newly installed listener become configurable without a matching app
16
- * release.
17
- *
18
- * The daemon serves a provider-scoped control surface. GET /listeners/<provider>/state to reconcile, POST
19
- * …/dispatch to wake an automation (optionally holding a turn-stream), …/failure + …/status to report. The
20
- * daemon holds no provider connection itself.
21
- *
22
- * WHAT DISPATCHES IT IS OPEN. A gateway process (contributes.processes) is the usual answer and the one the
23
- * reconcile feed is shaped for, it holds a live connection the daemon must not. But an extension BACKEND can
24
- * dispatch through the same route by declaring the dispatch path in `permissions.daemon`, which is
25
- * how an area that learns things on its own schedule (an estate poller noticing a container died) contributes
26
- * a trigger without running a gateway at all. Declaring this with neither is legal and inert: the source is
27
- * offered, and nothing ever fires it. */
11
+ // A realtime listener source the extension supplies: the daemon derives its accepted event types from `events` and
12
+ // folds `automation` into the trigger catalogue; the automation editor derives its source picker, filters and starter
13
+ // from the same data. The daemon serves a provider-scoped control surface (GET/POST /listeners/<provider>/...) and
14
+ // holds no provider connection itself. What dispatches it is open: a gateway process is the usual answer, but an
15
+ // extension backend may dispatch through the same route via `permissions.daemon`. Declaring this with neither is legal
16
+ // and inert.
28
17
  export const ListenerContributionSchema = z.object({
29
18
  provider: z
30
19
  .string()
@@ -45,8 +34,8 @@ export const ListenerContributionSchema = z.object({
45
34
  automation: z
46
35
  .object({
47
36
  label: z.string().min(1),
48
- // Only sources whose `message` events distinguish addressed messages declare this. Absent means the
49
- // generic editor offers no mention-only filter rather than inventing provider semantics.
37
+ // Only for a source whose message events distinguish being addressed; absent ⇒ no mention-only filter
38
+ // offered.
50
39
  mentionLabel: z
51
40
  .string()
52
41
  .min(1)
@@ -55,9 +44,8 @@ export const ListenerContributionSchema = z.object({
55
44
  "Only for a source whose message events distinguish being addressed. Absent ⇒ the editor offers no mention-only filter, rather than inventing semantics you did not promise.",
56
45
  ),
57
46
  channel: TriggerFieldContributionSchema.describe("The primary narrowing filter, a channel, a room, a repo."),
58
- // A SECOND narrowing axis, for a source whose events carry one, a pipeline's git ref, so a trigger can
59
- // say "the branch that ships" rather than "every agent's every failure". Absent ⇒ the editor offers
60
- // only the channel filter.
47
+ // A second narrowing axis, for a source whose events carry one (e.g. a pipeline's git ref); absent ⇒ only
48
+ // the channel filter is offered.
61
49
  branchField: TriggerFieldContributionSchema.optional().describe(
62
50
  'A second narrowing axis, for a source whose events carry one: a pipeline\'s git ref, so a trigger can say "the branch that ships" rather than "every agent\'s every failure".',
63
51
  ),
@@ -1,25 +1,21 @@
1
1
  import { z } from "zod";
2
2
  import type { ContributionPoint } from "../contribution-point.js";
3
3
 
4
- /* A custom file viewer the extension may register at runtime (api.viewers.register): the host resolves an open
5
- * file to this viewer by extension, gets its content, and renders the registered component with it, the host
6
- * keeps the fetch + open-file lifecycle and the daemon credentials; the extension only renders. This is the
7
- * non-sidebar contribution point. */
4
+ // A custom file viewer the extension may register at runtime; the host resolves an open file to it by extension,
5
+ // fetches the content, and renders the registered component. The host owns the fetch and open-file lifecycle and the
6
+ // daemon credentials; the extension only renders.
8
7
  export const ViewerContributionSchema = z.object({
9
8
  id: z.string().regex(/^[a-z0-9][a-z0-9-]*$/),
10
9
  extensions: z
11
10
  .array(z.string().regex(/^[a-z0-9]+$/))
12
11
  .min(1)
13
12
  .describe('Bare file extensions, no dot: e.g. ["docx", "xlsx"].'),
14
- /* `fetch` is how much of the file the host puts in the extension's hands, and it is a real choice:
15
- * text, decoded utf8 (`text` prop). For a format that IS text: svg, a subtitle track, a notebook.
16
- * blob, the whole file in memory (`blob` prop). For a format that must be parsed end to end before any of
17
- * it can be shown: a .docx, a spreadsheet. Bounded by the daemon's raw-read cap.
18
- * url , a streaming URL the component points an element at (`src` prop), never the bytes. For anything
19
- * RANGE-READ rather than parsed: audio and video, where the file may be gigabytes and the player
20
- * wants the header, the index and the seconds around the playhead, not the file. The host mints
21
- * the credential on that URL and keeps it out of the extension.
22
- */
13
+ // What the host hands the extension:
14
+ // text - decoded utf8, for a format that is text (svg, a subtitle track, a notebook).
15
+ // blob - the whole file in memory, for a format that must be parsed end to end (a .docx, a spreadsheet). Bounded by
16
+ // the daemon's raw-read cap.
17
+ // url - a streaming URL the component points an element at, for anything range-read rather than parsed (audio,
18
+ // video); the host mints the credential and keeps it out of the extension.
23
19
  fetch: z
24
20
  .enum(["text", "blob", "url"])
25
21
  .describe(
@@ -1,19 +1,12 @@
1
1
  import type { ExtensionManifest } from "./manifest.js";
2
2
 
3
- /* WHAT AN UPDATE ASKS FOR, MECHANICALLY. The install dialog renders a manifest's contributions once; an update
4
- * is judged on what sits BETWEEN two manifests, and "read both and compare" is exactly the job a person will
5
- * skip on the fifth update. So each manifest is folded to a set of POWERS, the consequential facts an owner
6
- * approved: which daemon routes it may call, which processes the daemon runs for it, what lands on the agent's
7
- * PATH, what may interrupt from another screen, each under a stable key (the identity compared) with a plain
8
- * sentence (what the reader sees). The diff is set arithmetic over the keys.
9
- *
10
- * Deliberately NOT here: plain settings (a new knob is config surface, not reach), display marks, category,
11
- * version. A power's INTERNALS moving (a process's command line, a fragment's contents) keeps its key, the
12
- * code changed, which is what the sha pin and the agent diff-read answer for; this diff answers only "did the
13
- * set of things I approved grow". An empty `added` is what makes an update one click. */
3
+ // What an update asks for, mechanically. Each manifest folds to a set of POWERS, the consequential facts an owner
4
+ // approved, under a stable key (compared) with a plain sentence (shown). The diff is set arithmetic over the keys.
5
+ // Deliberately excludes plain settings, display marks, category and version; a power's internals moving keeps its key,
6
+ // since the sha pin answers that question instead.
14
7
 
15
8
  export interface PowersDiff {
16
- // Powers the new manifest declares that the installed one didn't, the reason an update re-asks.
9
+ // Powers the new manifest declares that the installed one didn't; the reason an update re-asks.
17
10
  readonly added: string[];
18
11
  readonly removed: string[];
19
12
  readonly unchanged: string[];
@@ -80,8 +73,8 @@ const powersOf = (manifest: ExtensionManifest): Map<string, string> => {
80
73
  return powers;
81
74
  };
82
75
 
83
- // `before` absent covers a first install: everything the manifest declares is `added`, which is exactly what
84
- // the install dialog already renders, one vocabulary for both moments.
76
+ // `before` absent covers a first install: everything the manifest declares is `added`, the same vocabulary the install
77
+ // dialog already renders.
85
78
  export const diffPowers = (before: ExtensionManifest | undefined, after: ExtensionManifest): PowersDiff => {
86
79
  const from = before === undefined ? new Map<string, string>() : powersOf(before);
87
80
  const to = powersOf(after);