@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.
- package/README.md +61 -2
- package/dist/module.d.mts +23 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +37 -0
- package/dist/runtime/chat/index.d.ts +52 -0
- package/dist/runtime/chat/index.js +100 -0
- package/dist/runtime/chat/server.d.ts +36 -0
- package/dist/runtime/chat/server.js +27 -0
- package/dist/runtime/chat/types.d.ts +10 -0
- package/dist/runtime/chat/types.js +0 -0
- package/dist/runtime/client-capabilities.d.ts +29 -0
- package/dist/runtime/client-capabilities.js +25 -0
- package/dist/runtime/components-nuxt-ui/ForgeCapabilityApproval.d.vue.ts +2 -0
- package/dist/runtime/components-nuxt-ui/ForgeCapabilityApproval.vue +125 -0
- package/dist/runtime/components-nuxt-ui/ForgeCapabilityApproval.vue.d.ts +2 -0
- package/dist/runtime/components-nuxt-ui/ForgeCommandPalette.d.vue.ts +2 -0
- package/dist/runtime/components-nuxt-ui/ForgeCommandPalette.vue +218 -0
- package/dist/runtime/components-nuxt-ui/ForgeCommandPalette.vue.d.ts +2 -0
- package/dist/runtime/composables/defineClientCapability.d.ts +64 -0
- package/dist/runtime/composables/defineClientCapability.js +40 -0
- package/dist/runtime/composables/defineForgeAction.d.ts +82 -0
- package/dist/runtime/composables/defineForgeAction.js +147 -0
- package/dist/runtime/composables/useAgentCapabilities.d.ts +78 -0
- package/dist/runtime/composables/useAgentCapabilities.js +94 -0
- package/dist/runtime/composables/useCapabilityApproval.d.ts +109 -0
- package/dist/runtime/composables/useCapabilityApproval.js +74 -0
- package/dist/runtime/composables/useCapabilityInvocations.d.ts +150 -0
- package/dist/runtime/composables/useCapabilityInvocations.js +118 -0
- package/dist/runtime/composables/useCommandPalette.d.ts +28 -0
- package/dist/runtime/composables/useCommandPalette.js +49 -0
- package/dist/runtime/composables/usePaletteArgStep.d.ts +47 -0
- package/dist/runtime/composables/usePaletteArgStep.js +51 -0
- package/dist/runtime/internal/describe-error.d.ts +10 -0
- package/dist/runtime/internal/describe-error.js +12 -0
- package/dist/runtime/server/index.d.ts +7 -0
- package/dist/runtime/server/index.js +2 -0
- package/dist/runtime/server/utils/capability-registry.d.ts +35 -0
- package/dist/runtime/server/utils/capability-registry.js +24 -0
- package/dist/runtime/server/utils/client-capability-bridge.d.ts +647 -0
- package/dist/runtime/server/utils/client-capability-bridge.js +755 -0
- package/dist/runtime/shims-vue.d.ts +8 -0
- package/dist/runtime/types/client-capability.d.ts +159 -0
- package/dist/runtime/types/client-capability.js +0 -0
- package/dist/runtime/types/command-palette.d.ts +75 -0
- package/dist/runtime/types/command-palette.js +0 -0
- package/dist/runtime/types/forge-action.d.ts +152 -0
- package/dist/runtime/types/forge-action.js +0 -0
- package/dist/runtime/types/index.d.ts +3 -0
- package/dist/runtime/types/index.js +0 -0
- package/dist/types.d.mts +9 -0
- package/package.json +94 -4
- package/src/runtime/chat/index.ts +190 -0
- package/src/runtime/chat/server.ts +64 -0
- package/src/runtime/chat/types.ts +13 -0
- package/src/runtime/client-capabilities.ts +82 -0
- package/src/runtime/components-nuxt-ui/ForgeCapabilityApproval.vue +240 -0
- package/src/runtime/components-nuxt-ui/ForgeCommandPalette.vue +363 -0
- package/src/runtime/composables/defineClientCapability.ts +119 -0
- package/src/runtime/composables/defineForgeAction.ts +278 -0
- package/src/runtime/composables/useAgentCapabilities.ts +215 -0
- package/src/runtime/composables/useCapabilityApproval.ts +219 -0
- package/src/runtime/composables/useCapabilityInvocations.ts +325 -0
- package/src/runtime/composables/useCommandPalette.ts +97 -0
- package/src/runtime/composables/usePaletteArgStep.ts +110 -0
- package/src/runtime/internal/describe-error.ts +21 -0
- package/src/runtime/server/index.ts +8 -0
- package/src/runtime/server/utils/capability-registry.ts +66 -0
- package/src/runtime/server/utils/client-capability-bridge.ts +1201 -0
- package/src/runtime/shims-vue.d.ts +8 -0
- package/src/runtime/types/client-capability.ts +178 -0
- package/src/runtime/types/command-palette.ts +78 -0
- package/src/runtime/types/forge-action.ts +166 -0
- 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();
|