@skaile/forge-app-actions 0.0.0-stage → 0.2.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 (73) hide show
  1. package/README.md +61 -2
  2. package/dist/module.d.mts +23 -0
  3. package/dist/module.json +9 -0
  4. package/dist/module.mjs +37 -0
  5. package/dist/runtime/chat/index.d.ts +52 -0
  6. package/dist/runtime/chat/index.js +100 -0
  7. package/dist/runtime/chat/server.d.ts +36 -0
  8. package/dist/runtime/chat/server.js +27 -0
  9. package/dist/runtime/chat/types.d.ts +10 -0
  10. package/dist/runtime/chat/types.js +0 -0
  11. package/dist/runtime/client-capabilities.d.ts +29 -0
  12. package/dist/runtime/client-capabilities.js +25 -0
  13. package/dist/runtime/components-nuxt-ui/ForgeCapabilityApproval.d.vue.ts +2 -0
  14. package/dist/runtime/components-nuxt-ui/ForgeCapabilityApproval.vue +125 -0
  15. package/dist/runtime/components-nuxt-ui/ForgeCapabilityApproval.vue.d.ts +2 -0
  16. package/dist/runtime/components-nuxt-ui/ForgeCommandPalette.d.vue.ts +2 -0
  17. package/dist/runtime/components-nuxt-ui/ForgeCommandPalette.vue +218 -0
  18. package/dist/runtime/components-nuxt-ui/ForgeCommandPalette.vue.d.ts +2 -0
  19. package/dist/runtime/composables/defineClientCapability.d.ts +64 -0
  20. package/dist/runtime/composables/defineClientCapability.js +40 -0
  21. package/dist/runtime/composables/defineForgeAction.d.ts +82 -0
  22. package/dist/runtime/composables/defineForgeAction.js +147 -0
  23. package/dist/runtime/composables/useAgentCapabilities.d.ts +78 -0
  24. package/dist/runtime/composables/useAgentCapabilities.js +94 -0
  25. package/dist/runtime/composables/useCapabilityApproval.d.ts +109 -0
  26. package/dist/runtime/composables/useCapabilityApproval.js +74 -0
  27. package/dist/runtime/composables/useCapabilityInvocations.d.ts +150 -0
  28. package/dist/runtime/composables/useCapabilityInvocations.js +118 -0
  29. package/dist/runtime/composables/useCommandPalette.d.ts +28 -0
  30. package/dist/runtime/composables/useCommandPalette.js +49 -0
  31. package/dist/runtime/composables/usePaletteArgStep.d.ts +47 -0
  32. package/dist/runtime/composables/usePaletteArgStep.js +51 -0
  33. package/dist/runtime/internal/describe-error.d.ts +10 -0
  34. package/dist/runtime/internal/describe-error.js +12 -0
  35. package/dist/runtime/server/index.d.ts +7 -0
  36. package/dist/runtime/server/index.js +2 -0
  37. package/dist/runtime/server/utils/capability-registry.d.ts +35 -0
  38. package/dist/runtime/server/utils/capability-registry.js +24 -0
  39. package/dist/runtime/server/utils/client-capability-bridge.d.ts +647 -0
  40. package/dist/runtime/server/utils/client-capability-bridge.js +755 -0
  41. package/dist/runtime/shims-vue.d.ts +8 -0
  42. package/dist/runtime/types/client-capability.d.ts +159 -0
  43. package/dist/runtime/types/client-capability.js +0 -0
  44. package/dist/runtime/types/command-palette.d.ts +75 -0
  45. package/dist/runtime/types/command-palette.js +0 -0
  46. package/dist/runtime/types/forge-action.d.ts +152 -0
  47. package/dist/runtime/types/forge-action.js +0 -0
  48. package/dist/runtime/types/index.d.ts +3 -0
  49. package/dist/runtime/types/index.js +0 -0
  50. package/dist/types.d.mts +9 -0
  51. package/package.json +94 -4
  52. package/src/runtime/chat/index.ts +190 -0
  53. package/src/runtime/chat/server.ts +64 -0
  54. package/src/runtime/chat/types.ts +13 -0
  55. package/src/runtime/client-capabilities.ts +82 -0
  56. package/src/runtime/components-nuxt-ui/ForgeCapabilityApproval.vue +240 -0
  57. package/src/runtime/components-nuxt-ui/ForgeCommandPalette.vue +363 -0
  58. package/src/runtime/composables/defineClientCapability.ts +119 -0
  59. package/src/runtime/composables/defineForgeAction.ts +278 -0
  60. package/src/runtime/composables/useAgentCapabilities.ts +215 -0
  61. package/src/runtime/composables/useCapabilityApproval.ts +219 -0
  62. package/src/runtime/composables/useCapabilityInvocations.ts +325 -0
  63. package/src/runtime/composables/useCommandPalette.ts +97 -0
  64. package/src/runtime/composables/usePaletteArgStep.ts +110 -0
  65. package/src/runtime/internal/describe-error.ts +21 -0
  66. package/src/runtime/server/index.ts +8 -0
  67. package/src/runtime/server/utils/capability-registry.ts +66 -0
  68. package/src/runtime/server/utils/client-capability-bridge.ts +1201 -0
  69. package/src/runtime/shims-vue.d.ts +8 -0
  70. package/src/runtime/types/client-capability.ts +178 -0
  71. package/src/runtime/types/command-palette.ts +78 -0
  72. package/src/runtime/types/forge-action.ts +166 -0
  73. package/src/runtime/types/index.ts +24 -0
@@ -0,0 +1,110 @@
1
+ /**
2
+ * The command palette's **argument step**.
3
+ *
4
+ * A `PaletteAction` that declares `arg` needs one string before it can run
5
+ * (`defineForgeAction`'s `arg.toInput` turns that string into the action's
6
+ * typed input). Selecting such an action does not close the palette: it swaps
7
+ * the list for the action's candidates and waits for a second Enter.
8
+ *
9
+ * This holds the per-component state for that step. It lives here rather than
10
+ * inline in the components because both UI-kit variants of
11
+ * `ForgeCommandPalette` run it and are kept in deliberate lock-step — one copy
12
+ * cannot drift from the other if there is only one copy.
13
+ *
14
+ * Not a singleton: `useCommandPalette()` owns the shared registry, this owns
15
+ * one palette instance's transient step.
16
+ */
17
+
18
+ import { type ComputedRef, computed, type Ref, ref, type ShallowRef, shallowRef } from "vue";
19
+ import type { PaletteAction, PaletteArgOption, PaletteArgPrompt } from "../types/command-palette";
20
+
21
+ export interface UsePaletteArgStepReturn {
22
+ /** The action awaiting its argument, or `null` when the step is not active. */
23
+ action: ShallowRef<PaletteAction | null>;
24
+ /** Candidates for the current query. Empty for a free-text argument. */
25
+ options: ShallowRef<PaletteArgOption[]>;
26
+ /** Whether a candidate lookup is in flight. */
27
+ loading: Ref<boolean>;
28
+ /** Whether the palette is currently collecting an argument. */
29
+ active: ComputedRef<boolean>;
30
+ /** The active action's prompt declaration. */
31
+ prompt: ComputedRef<PaletteArgPrompt | null>;
32
+ /**
33
+ * Enter the argument step for `action`. Returns `false` — and changes
34
+ * nothing — when the action takes no argument, so a caller can use it as
35
+ * `if (!begin(a)) { close(); run(a) }`.
36
+ */
37
+ begin(action: PaletteAction): boolean;
38
+ /** Recompute the candidates for `query`. Stale responses are discarded. */
39
+ query(query: string): Promise<void>;
40
+ /** Leave the step without running anything. */
41
+ cancel(): void;
42
+ /**
43
+ * The string the action should run with, given what is typed and which row is
44
+ * highlighted — or `null` when the step cannot complete yet (nothing typed
45
+ * and nothing highlighted, or a typed value the prompt does not accept).
46
+ */
47
+ valueFor(typed: string, index: number): string | null;
48
+ }
49
+
50
+ export function usePaletteArgStep(): UsePaletteArgStepReturn {
51
+ const action = shallowRef<PaletteAction | null>(null);
52
+ const options = shallowRef<PaletteArgOption[]>([]);
53
+ const loading = ref(false);
54
+ // Monotonic ticket: an `options()` provider is async and the user keeps
55
+ // typing, so only the newest lookup may write. Same discipline as
56
+ // useFileSearch's AbortController, without assuming the provider does I/O.
57
+ let ticket = 0;
58
+
59
+ const active = computed(() => action.value !== null);
60
+ const prompt = computed(() => action.value?.arg ?? null);
61
+
62
+ function begin(next: PaletteAction): boolean {
63
+ if (!next.arg) return false;
64
+ action.value = next;
65
+ options.value = [];
66
+ void query("");
67
+ return true;
68
+ }
69
+
70
+ async function query(text: string): Promise<void> {
71
+ const arg = action.value?.arg;
72
+ if (!arg?.options) {
73
+ options.value = [];
74
+ return;
75
+ }
76
+ const mine = ++ticket;
77
+ loading.value = true;
78
+ try {
79
+ const found = await arg.options(text);
80
+ if (mine !== ticket) return; // superseded by a newer keystroke
81
+ options.value = found;
82
+ } catch {
83
+ // A failing lookup is "no candidates", never a broken palette. The user
84
+ // can still type a value when the prompt allows it.
85
+ if (mine === ticket) options.value = [];
86
+ } finally {
87
+ if (mine === ticket) loading.value = false;
88
+ }
89
+ }
90
+
91
+ function cancel(): void {
92
+ ticket++; // orphan any in-flight lookup
93
+ action.value = null;
94
+ options.value = [];
95
+ loading.value = false;
96
+ }
97
+
98
+ function valueFor(typed: string, index: number): string | null {
99
+ const arg = action.value?.arg;
100
+ if (!arg) return null;
101
+ const picked = options.value[index];
102
+ if (picked) return picked.value;
103
+ const allowCustom = arg.allowCustom ?? !arg.options;
104
+ const trimmed = typed.trim();
105
+ if (allowCustom && trimmed.length > 0) return trimmed;
106
+ return null;
107
+ }
108
+
109
+ return { action, options, loading, active, prompt, begin, query, cancel, valueFor };
110
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Turn a thrown value into the single line the agent (or model) will read.
3
+ *
4
+ * A zod v4 error carries the useful part in `issues`; `err.message` alone is a
5
+ * JSON blob. Flatten it to `path: message` pairs so the model can act on it.
6
+ *
7
+ * Lives outside `composables/` on purpose: that directory is auto-imported by
8
+ * the Nuxt module, and this helper is internal.
9
+ */
10
+ export function describeError(err: unknown): string {
11
+ if (err && typeof err === "object" && Array.isArray((err as { issues?: unknown }).issues)) {
12
+ const issues = (err as { issues: Array<{ path?: unknown[]; message?: string }> }).issues;
13
+ const lines = issues.map((issue) => {
14
+ const path = Array.isArray(issue.path) ? issue.path.join(".") : "";
15
+ return path ? `${path}: ${issue.message}` : String(issue.message);
16
+ });
17
+ if (lines.length > 0) return lines.join("; ");
18
+ }
19
+ if (err instanceof Error) return err.message;
20
+ return String(err);
21
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Explicit entry point for the server-side client-capability bridge, via the
3
+ * `./server` subpath. In a Nuxt consumer the same symbols are auto-imported
4
+ * (the module's `addServerImportsDir` on `runtime/server/utils`).
5
+ */
6
+
7
+ export * from "./utils/capability-registry";
8
+ export * from "./utils/client-capability-bridge";
@@ -0,0 +1,66 @@
1
+ // server/utils/capability-registry.ts
2
+ import type { AgentEvent, Capability } from "@skaile/workspaces/sdk/types";
3
+
4
+ /** Event sink handed to a capability handler when it runs. */
5
+ export interface CapabilityHandlerCtx {
6
+ /** Emit a synthetic AgentEvent into the app's SSE stream. */
7
+ emitEvent: (event: AgentEvent) => void;
8
+ }
9
+
10
+ export type CapabilityHandler = (
11
+ input: Record<string, unknown>,
12
+ ctx: CapabilityHandlerCtx,
13
+ ) => Promise<unknown>;
14
+
15
+ export interface CapabilityEntry {
16
+ capability: Capability;
17
+ handler: CapabilityHandler;
18
+ }
19
+
20
+ /**
21
+ * Plain-TS capability registry for the forge app — holds client-side v2
22
+ * capabilities and routes `capability_invoked` calls to their handlers.
23
+ */
24
+ export class CapabilityRegistry {
25
+ private entries = new Map<string, CapabilityEntry>();
26
+
27
+ register(entry: CapabilityEntry): void {
28
+ this.entries.set(entry.capability.name, entry);
29
+ }
30
+
31
+ list(): Capability[] {
32
+ return [...this.entries.values()].map((e) => e.capability);
33
+ }
34
+
35
+ async dispatch(
36
+ name: string,
37
+ input: Record<string, unknown>,
38
+ ctx: CapabilityHandlerCtx,
39
+ ): Promise<unknown | { error: string }> {
40
+ const entry = this.entries.get(name);
41
+ if (!entry) return { error: `Unknown capability: ${name}` };
42
+ try {
43
+ return await entry.handler(input, ctx);
44
+ } catch (err) {
45
+ return { error: err instanceof Error ? err.message : String(err) };
46
+ }
47
+ }
48
+ }
49
+
50
+ /** Dispatch a `capability_invoked` event and send back its `capability_result`. */
51
+ export async function handleCapabilityInvoked(
52
+ event: { callId: string; name: string; input: unknown },
53
+ deps: {
54
+ registry: CapabilityRegistry;
55
+ ctx: CapabilityHandlerCtx;
56
+ sendResult: (callId: string, result: unknown) => Promise<void> | void;
57
+ },
58
+ ): Promise<void> {
59
+ const input =
60
+ event.input && typeof event.input === "object" ? (event.input as Record<string, unknown>) : {};
61
+ const result = await deps.registry.dispatch(event.name, input, deps.ctx);
62
+ await deps.sendResult(event.callId, result);
63
+ }
64
+
65
+ /** Process-wide singleton — capabilities are registered once at module load. */
66
+ export const capabilityRegistry = new CapabilityRegistry();