@schlenkr/ragents 0.1.14 → 0.1.15
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/apps/server/package.json +1 -1
- package/apps/server/src/api/contracts.ts +19 -1
- package/apps/server/src/api/core-methods.ts +12 -0
- package/apps/server/src/chat-handler.ts +19 -0
- package/apps/server/src/host-api.json +2 -0
- package/apps/server/src/host-api.ts +2 -2
- package/apps/server/src/plugin-build/build.ts +1 -1
- package/apps/server/src/plugin-support/actor-programs/app-project.ts +61 -17
- package/apps/server/src/plugin-support/actor-programs/contract.ts +18 -1
- package/apps/server/src/plugin-support/actor-programs/service.ts +22 -1
- package/apps/server/src/plugin-support/plugin-folder.ts +14 -1
- package/apps/server/src/plugin-support/run-scripts.ts +27 -2
- package/apps/server/src/profile/compose.ts +2 -0
- package/apps/server/src/provider.ts +23 -4
- package/apps/server/src/ragents/chat-projection.ts +7 -0
- package/apps/server/src/ragents/global-chat.ts +5 -1
- package/apps/server/src/ragents/run-script-tools.ts +66 -0
- package/apps/server/src/ragents/session.ts +122 -60
- package/apps/web/dist/assets/{highlighted-body-KPVGNVTW-BslIwn_y.js → highlighted-body-KPVGNVTW-BwD4uYEe.js} +1 -1
- package/apps/web/dist/assets/{main-BUN8m8SW.js → main-KBrc5Q77.js} +1 -1
- package/apps/web/dist/assets/{mermaid-HWGCJPDP-B1erZlFM.js → mermaid-HWGCJPDP-CSCkaArr.js} +88 -88
- package/apps/web/dist/assets/runPanel-BjG5NBKm.js +26 -0
- package/apps/web/dist/help/guide-clients.html +21 -4
- package/apps/web/dist/help/guide-clients.md +23 -4
- package/apps/web/dist/help/guide-plugins.html +12 -2
- package/apps/web/dist/help/guide-plugins.md +9 -2
- package/apps/web/dist/help/guide-programs.html +8 -2
- package/apps/web/dist/help/guide-programs.md +9 -2
- package/apps/web/dist/help/guide-runtime.html +3 -2
- package/apps/web/dist/help/guide-runtime.md +3 -2
- package/apps/web/dist/help/index.html +1 -1
- package/apps/web/dist/help/mini-app.css +7 -2
- package/apps/web/dist/host-web.json +6 -6
- package/apps/web/dist/index.html +2 -2
- package/apps/web/dist/run-panel.html +2 -2
- package/apps/web/package.json +1 -1
- package/apps/web/src/run-panel/RunPanelApp.tsx +1 -1
- package/apps/web/src/run-panel/RunPanelMenu.tsx +47 -11
- package/bundles/ragents.activity/ragents-bundle.json +1 -1
- package/bundles/ragents.actor-programs/ragents-bundle.json +4 -2
- package/bundles/ragents.actor-programs/server/index.js +437 -24
- package/bundles/ragents.actor-programs/server/index.js.map +4 -4
- package/bundles/ragents.actor-programs/web/index.js.map +1 -1
- package/bundles/ragents.ask/ragents-bundle.json +2 -2
- package/bundles/ragents.ask/server/index.js +1 -0
- package/bundles/ragents.ask/server/index.js.map +2 -2
- package/bundles/ragents.browser/ragents-bundle.json +1 -1
- package/bundles/ragents.documents/ragents-bundle.json +1 -1
- package/bundles/ragents.lsp-fsharp/ragents-bundle.json +1 -1
- package/bundles/ragents.lsp-roslyn/ragents-bundle.json +1 -1
- package/bundles/ragents.lsp-typescript/ragents-bundle.json +1 -1
- package/bundles/ragents.model-relay/ragents-bundle.json +1 -1
- package/bundles/ragents.orchestration/ragents-bundle.json +7 -4
- package/bundles/ragents.orchestration/server/chunks/chunk-QS3WJ3D7.js +11 -0
- package/bundles/ragents.orchestration/server/chunks/chunk-QS3WJ3D7.js.map +7 -0
- package/bundles/ragents.orchestration/server/exports/server/contract.js +9 -0
- package/bundles/ragents.orchestration/server/exports/server/contract.js.map +7 -0
- package/bundles/ragents.orchestration/server/index.js +49 -3
- package/bundles/ragents.orchestration/server/index.js.map +2 -2
- package/bundles/ragents.orchestration/surface.hbs +5 -0
- package/bundles/ragents.overseer/ragents-bundle.json +1 -1
- package/bundles/ragents.processes/ragents-bundle.json +1 -1
- package/bundles/ragents.product/ragents-bundle.json +1 -1
- package/bundles/ragents.profile-distribution/ragents-bundle.json +1 -1
- package/bundles/ragents.reference/actors/notebook/package.json +16 -0
- package/bundles/ragents.reference/actors/notebook/src/client.tsx +31 -0
- package/bundles/ragents.reference/actors/notebook/src/server.ts +27 -0
- package/bundles/ragents.reference/actors/notebook/tests/program.test.ts +27 -0
- package/bundles/ragents.reference/ragents-bundle.json +4 -3
- package/bundles/ragents.reference/run-scripts/quick-note/RUN.md +14 -0
- package/bundles/ragents.reference/run-scripts/quick-note/package.json +9 -0
- package/bundles/ragents.reference/run-scripts/quick-note/src/server.ts +29 -0
- package/bundles/ragents.reference/run-scripts/quick-note/tests/program.test.ts +43 -0
- package/bundles/ragents.reference/run-scripts/run-roster/RUN.md +19 -0
- package/bundles/ragents.reference/run-scripts/run-roster/package.json +9 -0
- package/bundles/ragents.reference/run-scripts/run-roster/src/server.ts +26 -0
- package/bundles/ragents.reference/run-scripts/run-roster/tests/program.test.ts +47 -0
- package/bundles/ragents.todo/ragents-bundle.json +1 -1
- package/bundles/ragents.transcript/ragents-bundle.json +1 -1
- package/bundles/ragents.watch/ragents-bundle.json +1 -1
- package/bundles/ragents.workspace/ragents-bundle.json +1 -1
- package/package.json +3 -3
- package/packages/ragents/src/domain/vocabulary.ts +4 -0
- package/packages/ragents/src/index.ts +3 -1
- package/packages/ragents/src/plugin-host.ts +62 -10
- package/packages/ragents/src/plugin-types.ts +11 -0
- package/packages/ragents/src/runtime/guards.ts +2 -1
- package/packages/ragents/src/typescript/async-compiler.ts +11 -1
- package/plugins/ragents.actor-programs/server/index.ts +3 -0
- package/plugins/ragents.actor-programs/server/runtime.ts +424 -28
- package/plugins/ragents.actor-programs/server/script-state.ts +54 -0
- package/plugins/ragents.actor-programs/server/tool-contributor.ts +2 -0
- package/plugins/ragents.ask/server/ask-tool.ts +1 -0
- package/plugins/ragents.orchestration/ragents-plugin.json +1 -0
- package/plugins/ragents.orchestration/server/contract.ts +14 -0
- package/plugins/ragents.orchestration/server/index.ts +4 -1
- package/plugins/ragents.orchestration/server/surface-tool.ts +58 -2
- package/plugins/ragents.orchestration/surface.hbs +5 -0
- package/plugins/ragents.reference/actors/notebook/package.json +16 -0
- package/plugins/ragents.reference/actors/notebook/src/client.tsx +31 -0
- package/plugins/ragents.reference/actors/notebook/src/server.ts +27 -0
- package/plugins/ragents.reference/actors/notebook/tests/program.test.ts +27 -0
- package/plugins/ragents.reference/run-scripts/quick-note/RUN.md +14 -0
- package/plugins/ragents.reference/run-scripts/quick-note/package.json +9 -0
- package/plugins/ragents.reference/run-scripts/quick-note/src/server.ts +29 -0
- package/plugins/ragents.reference/run-scripts/quick-note/tests/program.test.ts +43 -0
- package/plugins/ragents.reference/run-scripts/run-roster/RUN.md +19 -0
- package/plugins/ragents.reference/run-scripts/run-roster/package.json +9 -0
- package/plugins/ragents.reference/run-scripts/run-roster/src/server.ts +26 -0
- package/plugins/ragents.reference/run-scripts/run-roster/tests/program.test.ts +47 -0
- package/scripts/agent/agent-cli.ts +46 -2
- package/scripts/package/ragents.mjs +3 -1
- package/apps/web/dist/assets/runPanel-s8gGpr9M.js +0 -21
|
@@ -10,6 +10,7 @@ const metadata = [
|
|
|
10
10
|
{ name: "actor_program_remove", label: "Remove Actor Program", description: "Detach functions and views and stop the backend.", longDescription: "A TypeScript actor is stopped; an LLM actor retains its agent behavior." },
|
|
11
11
|
{ name: "actor_view_set_visibility", label: "Show or hide Actor View", description: "Set surface visibility by package-name/view-key or @handle/view-key.", longDescription: "Use names you chose; the server resolves the view ID. A unique view title also works." },
|
|
12
12
|
{ name: "actor_program_diagnostics", label: "Actor Program Diagnostics", description: "Read the last project diagnostics.", longDescription: "Changed errors are automatically supplied before the next model request." },
|
|
13
|
+
{ name: "actor_program_ensure", label: "Ensure Actor Program", description: "Make a package active once: return it when active, restart its stopped actor, activate it when installed, or install a shared package of the profile.", longDescription: "Safe to call on every start: an active package is returned unchanged and not rebuilt. status names what happened. actor_program_activate instead rebuilds and reactivates." },
|
|
13
14
|
] as const;
|
|
14
15
|
export const actorProgramManagementToolNames = metadata.map((entry) => entry.name);
|
|
15
16
|
export const actorProgramToolsAvailable = defineToolAvailability({ availability: "conditional", availabilityDetail: "For executable actors with agent.spawn and plugin.state.write." }, actor => actor.kind !== "human" && holdsUsable(actor, "agent.spawn") && holdsUsable(actor, "plugin.state.write"));
|
|
@@ -22,6 +23,7 @@ const managementTools = (runtime: ActorProgramRuntime, diagnostics: Diagnostics)
|
|
|
22
23
|
defineRunFunction({ ...metadata[3], schema: Type.Object({ name }, { additionalProperties: false }), resultSchema: Type.Object({ removed: Type.String() }, { additionalProperties: false }), available: actorProgramToolsAvailable, executionMode: "sequential", run: (scope, id, input) => runtime.remove(scope.context(id), scope.caller.runId, input.name) }),
|
|
23
24
|
defineRunFunction({ ...metadata[4], schema: Type.Object({ view: Type.String({ minLength: 1, description: "package-name/view-key or @handle/view-key of an activated view, without the surface entity prefix app:. No generated IDs needed." }), visible: Type.Boolean() }, { additionalProperties: false }), resultSchema: Type.Object({ view: Type.String(), visible: Type.Boolean() }, { additionalProperties: false }), available: actorProgramToolsAvailable, executionMode: "sequential", run: (scope, id, input) => runtime.setVisibility(scope.context(id), scope.caller.runId, input.view, input.visible) }),
|
|
24
25
|
defineRunFunction({ ...metadata[5], schema: Type.Object({ name: Type.Optional(name) }, { additionalProperties: false }), resultSchema: Type.String(), available: actorProgramToolsAvailable, executionMode: "sequential", run: (scope, _id, input) => diagnostics.latest(scope.caller.runId, scope.caller.actorId, input.name) }),
|
|
26
|
+
defineRunFunction({ ...metadata[6], schema: Type.Object({ name }, { additionalProperties: false }), resultSchema: Type.Object({ actorId: Type.String(), handle: Type.String(), status: Type.Union([Type.Literal("active"), Type.Literal("restarted"), Type.Literal("activated"), Type.Literal("installed")]) }, { additionalProperties: false }), available: actorProgramToolsAvailable, executionMode: "sequential", run: (scope, id, input) => runtime.ensure(scope.context(id), scope.caller.runId, input.name, scope.signal) }),
|
|
25
27
|
];
|
|
26
28
|
export const createActorProgramToolContributors = (runtime: ActorProgramRuntime, diagnostics: Diagnostics): readonly ToolContributor[] => [
|
|
27
29
|
{ name: "ragents.actor-programs.management", descriptors: metadata.map((item) => toolDescriptorFrom(item, actorProgramToolsAvailable)), tools: () => managementTools(runtime, diagnostics) },
|
|
@@ -6,6 +6,7 @@ import type { AskService } from "./contract.js";
|
|
|
6
6
|
export const askToolMetadata = {
|
|
7
7
|
name: "ask_user",
|
|
8
8
|
label: "Question",
|
|
9
|
+
nativeTool: true,
|
|
9
10
|
description: "Asks the user for a required decision with answer options and waits for the answer.",
|
|
10
11
|
longDescription: "Always use this tool when you need a decision from the user "
|
|
11
12
|
+ "(e.g. choosing a branch or a time range), instead of only asking the question as text. "
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { serviceToken, type CommandContext, type ServiceToken } from "@ragents/engine";
|
|
2
|
+
|
|
3
|
+
export interface SurfacePlacementRequest {
|
|
4
|
+
entity: string;
|
|
5
|
+
direction?: "horizontal" | "vertical";
|
|
6
|
+
weight?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Places an entity next to a run's surface layout, as canvas_layout_place does; an entity the layout shows already stays. */
|
|
10
|
+
export interface SurfacePlacement {
|
|
11
|
+
place: (context: CommandContext, runId: string, request: SurfacePlacementRequest) => { placed: boolean };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const surfacePlacementToken: ServiceToken<SurfacePlacement> = serviceToken("ragents.orchestration.surface-placement");
|
|
@@ -11,7 +11,8 @@ import { runtimeProviderToken } from "@ragents/host/ragents/host-services.js";
|
|
|
11
11
|
import { pluginAsset } from "@ragents/host/plugin-support/plugin-folder.js";
|
|
12
12
|
import type { PluginModule } from "@ragents/host/plugin-support/plugin-module.js";
|
|
13
13
|
import { boundToTools, handlebarsPrompt } from "@ragents/host/plugin-support/prompt.js";
|
|
14
|
-
import { createSurfaceToolContributor } from "./surface-tool.js";
|
|
14
|
+
import { createSurfaceToolContributor, placeOnSurface } from "./surface-tool.js";
|
|
15
|
+
import { surfacePlacementToken } from "./contract.js";
|
|
15
16
|
import { createRunStopContributor } from "./run-stop-tool.js";
|
|
16
17
|
import { runManagementToken } from "@ragents/host/ragents/global-chat.js";
|
|
17
18
|
|
|
@@ -19,6 +20,7 @@ const orchestrationPlugin: RAgentsPlugin = {
|
|
|
19
20
|
manifest: { id: "ragents.orchestration" },
|
|
20
21
|
register: (host) => {
|
|
21
22
|
host.functions(createSurfaceToolContributor());
|
|
23
|
+
host.provide(surfacePlacementToken, placeOnSurface(host.service(runtimeProviderToken)));
|
|
22
24
|
host.functions(createRunStopContributor(
|
|
23
25
|
(runId) => host.service(runManagementToken)().stop(runId),
|
|
24
26
|
(error) => console.error("Run stop by the coordinator failed", error),
|
|
@@ -53,6 +55,7 @@ const orchestrationPlugin: RAgentsPlugin = {
|
|
|
53
55
|
boundToTools(
|
|
54
56
|
handlebarsPrompt("ragents.orchestration.surface", 650, pluginAsset("ragents.orchestration", "surface.hbs")),
|
|
55
57
|
"canvas_layout_replace",
|
|
58
|
+
"canvas_layout_place",
|
|
56
59
|
),
|
|
57
60
|
);
|
|
58
61
|
},
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
defineRunFunction,
|
|
6
6
|
defineToolAvailability,
|
|
7
7
|
holdsUsable,
|
|
8
|
+
type CommandContext,
|
|
9
|
+
type Orchestration,
|
|
8
10
|
type RunFunction,
|
|
9
11
|
type RunView,
|
|
10
12
|
type ToolContributor,
|
|
@@ -18,6 +20,7 @@ import {
|
|
|
18
20
|
type SurfaceLayout,
|
|
19
21
|
} from "../contract.js";
|
|
20
22
|
import { surfaceTileEntities, mapSurfaceTileEntities } from "../tiled-layout.js";
|
|
23
|
+
import type { SurfacePlacement, SurfacePlacementRequest } from "./contract.js";
|
|
21
24
|
|
|
22
25
|
export const surfaceToolMetadata = {
|
|
23
26
|
name: "canvas_layout_replace",
|
|
@@ -34,6 +37,17 @@ export const surfaceToolMetadata = {
|
|
|
34
37
|
+ "Participants you do not place stay reachable through the run header.",
|
|
35
38
|
} as const;
|
|
36
39
|
|
|
40
|
+
export const placeToolMetadata = {
|
|
41
|
+
name: "canvas_layout_place",
|
|
42
|
+
label: "Place on Surface",
|
|
43
|
+
description: "Place one actor or mini-app next to the current surface layout instead of replacing it.",
|
|
44
|
+
longDescription: "entity is {entity} of a tile: '@helper' or 'app:@workspace/main'. The current arrangement becomes one side of a new split "
|
|
45
|
+
+ "and the entity the other: direction 'horizontal' (default) puts it to the right, 'vertical' below. weight is its share against "
|
|
46
|
+
+ "the existing arrangement's weight 1, default 1. An entity the layout already shows stays where it is; placed is then false. "
|
|
47
|
+
+ "On an empty surface the entity becomes the only tile. Use it when you join a running run, such as a run script started with "
|
|
48
|
+
+ "start.embedded, so that the tiles others arranged stay.",
|
|
49
|
+
} as const;
|
|
50
|
+
|
|
37
51
|
export const canDesignSurface = defineToolAvailability({
|
|
38
52
|
availability: "conditional",
|
|
39
53
|
availabilityDetail: "Only for agents and TypeScript actors with the capability plugin.state.write.",
|
|
@@ -117,6 +131,48 @@ export const checkLayoutAgainstRun = (view: RunView, input: SurfaceLayout): Surf
|
|
|
117
131
|
return layout;
|
|
118
132
|
};
|
|
119
133
|
|
|
134
|
+
const currentLayout = (view: RunView): SurfaceLayout => {
|
|
135
|
+
const state = view.pluginStates.find((entry) => entry.pluginId === ORCHESTRATION_PLUGIN_ID && entry.scope.kind === "run")?.state;
|
|
136
|
+
return state === undefined || state === null ? { root: null } : surfaceLayoutOf(state);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/** Splits the root once: the existing arrangement keeps weight 1, the placed tile gets its own. */
|
|
140
|
+
const placed = (view: RunView, request: SurfacePlacementRequest): SurfaceLayout | undefined => {
|
|
141
|
+
const current = currentLayout(view);
|
|
142
|
+
const [tile] = surfaceTileEntities(checkLayoutAgainstRun(view, parsedLayout({ root: { entity: request.entity } })).root);
|
|
143
|
+
if (surfaceTileEntities(current.root).includes(tile!)) return undefined;
|
|
144
|
+
const root = current.root === null ? { entity: tile! } : {
|
|
145
|
+
direction: request.direction ?? "horizontal",
|
|
146
|
+
weights: [1, request.weight ?? 1],
|
|
147
|
+
children: [current.root, { entity: tile! }],
|
|
148
|
+
};
|
|
149
|
+
return parsedLayout({ root });
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export const placeOnSurface = (runtime: () => Orchestration): SurfacePlacement => ({
|
|
153
|
+
place: (context: CommandContext, runId: string, request: SurfacePlacementRequest) => {
|
|
154
|
+
const layout = placed(runtime().view(runId), request);
|
|
155
|
+
if (!layout) return { placed: false };
|
|
156
|
+
runtime().replacePluginState(context, runId, { pluginId: ORCHESTRATION_PLUGIN_ID, scope: { kind: "run" }, state: layout });
|
|
157
|
+
return { placed: true };
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const placeSchema = Type.Object({
|
|
162
|
+
entity: Type.String({ minLength: 1, description: "Actor @handle or activated mini-app app:@handle/view-key or app:program-name/view-key" }),
|
|
163
|
+
direction: Type.Optional(Type.Union([Type.Literal("horizontal"), Type.Literal("vertical")], { description: "horizontal: to the right of the current layout (default); vertical: below it" })),
|
|
164
|
+
weight: Type.Optional(Type.Number({ exclusiveMinimum: 0, description: "Share of the placed tile against the existing arrangement's weight 1; default 1" })),
|
|
165
|
+
}, { additionalProperties: false });
|
|
166
|
+
|
|
167
|
+
export const createPlaceTool = (): RunFunction =>
|
|
168
|
+
defineRunFunction({
|
|
169
|
+
...placeToolMetadata,
|
|
170
|
+
schema: placeSchema,
|
|
171
|
+
resultSchema: Type.Object({ placed: Type.Boolean() }, { additionalProperties: false }),
|
|
172
|
+
available: canDesignSurface,
|
|
173
|
+
run: ({ runtime, caller, context }, toolCallId, input) => placeOnSurface(() => runtime).place(context(toolCallId), caller.runId, input),
|
|
174
|
+
});
|
|
175
|
+
|
|
120
176
|
export const createSurfaceTool = (): RunFunction =>
|
|
121
177
|
defineRunFunction({
|
|
122
178
|
...surfaceToolMetadata,
|
|
@@ -137,6 +193,6 @@ export const createSurfaceTool = (): RunFunction =>
|
|
|
137
193
|
|
|
138
194
|
export const createSurfaceToolContributor = (): ToolContributor => ({
|
|
139
195
|
name: "ragents.orchestration.surface",
|
|
140
|
-
descriptors: [toolDescriptorFrom(surfaceToolMetadata, canDesignSurface)],
|
|
141
|
-
tools: () => [createSurfaceTool()],
|
|
196
|
+
descriptors: [toolDescriptorFrom(surfaceToolMetadata, canDesignSurface), toolDescriptorFrom(placeToolMetadata, canDesignSurface)],
|
|
197
|
+
tools: () => [createSurfaceTool(), createPlaceTool()],
|
|
142
198
|
});
|
|
@@ -19,6 +19,11 @@ App left, chat right at 50:50:
|
|
|
19
19
|
]}}. Resolve the actual actor and app names before calling; the example names are not existing
|
|
20
20
|
participants.
|
|
21
21
|
|
|
22
|
+
To add one tile without replacing what is arranged, `context.functions.canvas_layout_place({ entity })`
|
|
23
|
+
splits the current layout once and puts the entity to its right, or below with `direction: "vertical"`;
|
|
24
|
+
`weight` is its share against the existing arrangement's weight 1. An entity the layout shows already
|
|
25
|
+
stays where it is. A run script started inside a running run (`start.embedded`) uses place, not replace.
|
|
26
|
+
|
|
22
27
|
An actor tile carries {entity:"@helper",chatInput:false} when its chat composer should stay hidden.
|
|
23
28
|
That changes neither permissions nor the inspector. Mini-app tiles have no chatInput.
|
|
24
29
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "notebook",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"ragents": {
|
|
6
|
+
"title": "Notebook",
|
|
7
|
+
"description": "Keeps the notes that run scripts send it, newest first.",
|
|
8
|
+
"backend": "src/server.ts",
|
|
9
|
+
"views": [
|
|
10
|
+
{
|
|
11
|
+
"id": "main",
|
|
12
|
+
"client": "src/client.tsx"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { createRoot } from "react-dom/client";
|
|
3
|
+
import { context, useAppState } from "@ragents/client";
|
|
4
|
+
import * as UI from "@ragents/client/ui";
|
|
5
|
+
|
|
6
|
+
const App = () => {
|
|
7
|
+
const state = useAppState();
|
|
8
|
+
const notes = state.notes ?? [];
|
|
9
|
+
const [status, setStatus] = React.useState("Run scripts add their notes here.");
|
|
10
|
+
|
|
11
|
+
const clear = async (): Promise<void> => {
|
|
12
|
+
const answer = await context.capabilities.call("clear", {});
|
|
13
|
+
setStatus(`Cleared ${answer.cleared} ${answer.cleared === 1 ? "note" : "notes"}.`);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<UI.AppLayout title="Notebook" description={<>{notes.length} {notes.length === 1 ? "note" : "notes"}</>}>
|
|
18
|
+
<UI.Stack>
|
|
19
|
+
<p className="text-muted-foreground" role="status">{status}</p>
|
|
20
|
+
<ul className="border-t border-border">
|
|
21
|
+
{notes.map((note, index) => <li className="min-h-8 border-b border-border-soft py-1.5 [overflow-wrap:anywhere]" key={index}>{note}</li>)}
|
|
22
|
+
</ul>
|
|
23
|
+
<UI.Button disabled={notes.length === 0} onClick={() => void clear()} variant="outline">Clear</UI.Button>
|
|
24
|
+
</UI.Stack>
|
|
25
|
+
</UI.AppLayout>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const root = document.getElementById("root");
|
|
30
|
+
if (!root) throw new Error("The #root element is missing.");
|
|
31
|
+
createRoot(root).render(<App />);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineActor } from "@ragents/server";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
|
|
4
|
+
const KEPT_NOTES = 50;
|
|
5
|
+
|
|
6
|
+
const contract = {
|
|
7
|
+
state: Type.Object({ notes: Type.Optional(Type.Array(Type.String())) }, { additionalProperties: false }),
|
|
8
|
+
functions: {
|
|
9
|
+
clear: { label: "Clear notes", input: Type.Object({}, { additionalProperties: false }), output: Type.Object({ cleared: Type.Integer() }, { additionalProperties: false }) },
|
|
10
|
+
},
|
|
11
|
+
input: { capabilities: [] },
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
14
|
+
export default defineActor(contract, {
|
|
15
|
+
functions: {
|
|
16
|
+
clear: (_input, context) => {
|
|
17
|
+
const cleared = context.state.read().notes?.length ?? 0;
|
|
18
|
+
context.state.replace({ notes: [] });
|
|
19
|
+
return { cleared };
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
onInput: (input, context) => {
|
|
23
|
+
const note = input.content.trim();
|
|
24
|
+
if (!note) throw new Error("A note needs text.");
|
|
25
|
+
context.state.replace({ notes: [note, ...context.state.read().notes ?? []].slice(0, KEPT_NOTES) });
|
|
26
|
+
},
|
|
27
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { createTestContext } from "@ragents/server/testing";
|
|
4
|
+
import program from "../src/server.ts";
|
|
5
|
+
|
|
6
|
+
const input = (content: string) => ({ id: "input-1", content, artifactIds: [], sourceEventIds: [], subscriptionId: null, event: null });
|
|
7
|
+
|
|
8
|
+
test("keeps notes newest first and at most fifty", async () => {
|
|
9
|
+
const context = createTestContext<{ notes?: string[] }>({ state: { notes: Array.from({ length: 50 }, (_, index) => `old ${index}`) } });
|
|
10
|
+
await program.onInput!(input(" new note "), context);
|
|
11
|
+
const notes = context.state.read().notes!;
|
|
12
|
+
assert.equal(notes.length, 50);
|
|
13
|
+
assert.equal(notes[0], "new note");
|
|
14
|
+
assert.equal(notes.at(-1), "old 48");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("an empty note is refused and changes nothing", async () => {
|
|
18
|
+
const context = createTestContext<{ notes?: string[] }>({ state: { notes: ["kept"] } });
|
|
19
|
+
await assert.rejects(async () => program.onInput!(input(" "), context), /needs text/);
|
|
20
|
+
assert.deepEqual(context.state.read(), { notes: ["kept"] });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("clear empties the notebook and names how many notes it removed", async () => {
|
|
24
|
+
const context = createTestContext<{ notes?: string[] }>({ state: { notes: ["one", "two"] } });
|
|
25
|
+
assert.deepEqual(await program.functions.clear({}, context), { cleared: 2 });
|
|
26
|
+
assert.deepEqual(context.state.read(), { notes: [] });
|
|
27
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Add a quick note
|
|
3
|
+
description: "A prepared one-step script shows a shared actor package: it adds a note to the notebook it shares with the roster check, in a new or a running run."
|
|
4
|
+
order: 180
|
|
5
|
+
coordinator: false
|
|
6
|
+
embeddable: true
|
|
7
|
+
shared-programs: notebook
|
|
8
|
+
tags: Run scripts, Concept demo, TypeScript actors
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
The start value `{ "text": "..." }` is the note; without one, the script notes when it was
|
|
12
|
+
started. The script makes the shared notebook active with `actor_program_ensure`, sends it the
|
|
13
|
+
note, places the notebook's view with `canvas_layout_place`, and ends the start with
|
|
14
|
+
`context.finish`. Whether the roster check or this script comes first, the run has one notebook.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { defineActor } from "@ragents/server";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
|
|
4
|
+
const contract = {
|
|
5
|
+
state: Type.Object({}, { additionalProperties: false }),
|
|
6
|
+
functions: {},
|
|
7
|
+
input: { capabilities: ["actor_program_ensure", "actor_input", "canvas_layout_place"] },
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
const noteOf = (input: unknown, now: string): string => {
|
|
11
|
+
if (input === null) return `Started at ${now}.`;
|
|
12
|
+
const text = typeof input === "object" && input !== null && "text" in input ? (input as { text: unknown }).text : undefined;
|
|
13
|
+
if (typeof text !== "string" || !text.trim()) throw new Error('The start value is { "text": "..." } with the note, or none.');
|
|
14
|
+
return text.trim();
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default defineActor(contract, {
|
|
18
|
+
functions: {},
|
|
19
|
+
onInput: () => {
|
|
20
|
+
throw new Error("The quick note answers only starts; start it again with the note as its start value.");
|
|
21
|
+
},
|
|
22
|
+
onStart: async (start, context) => {
|
|
23
|
+
const note = noteOf(start.input, context.std.now());
|
|
24
|
+
const notebook = await context.functions.actor_program_ensure({ name: "notebook" });
|
|
25
|
+
await context.functions.actor_input({ actor: `@${notebook.handle}`, content: note });
|
|
26
|
+
await context.functions.canvas_layout_place({ entity: "app:notebook/main" });
|
|
27
|
+
context.finish({ note, notebook: notebook.status }, { summary: `Noted: ${note}` });
|
|
28
|
+
},
|
|
29
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { createTestContext } from "@ragents/server/testing";
|
|
4
|
+
import program from "../src/server.ts";
|
|
5
|
+
|
|
6
|
+
const start = (input: unknown) => ({ input, options: {}, embedded: true, startedBy: "owner-id", count: 1 });
|
|
7
|
+
|
|
8
|
+
const note = (status: "active" | "installed" = "installed") => {
|
|
9
|
+
const calls: { name: string; input: unknown }[] = [];
|
|
10
|
+
const record = <T>(name: string, answer: T) => (input: unknown): T => { calls.push({ name, input }); return answer; };
|
|
11
|
+
const context = createTestContext<Record<string, never>>({ state: {}, functions: {
|
|
12
|
+
actor_program_ensure: record("actor_program_ensure", { actorId: "id-notebook", handle: "notebook", status }),
|
|
13
|
+
actor_input: record("actor_input", []),
|
|
14
|
+
canvas_layout_place: record("canvas_layout_place", { placed: status === "installed" }),
|
|
15
|
+
} });
|
|
16
|
+
return { calls, context };
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
test("sends the note to the shared notebook and reports it", async () => {
|
|
20
|
+
const { calls, context } = note();
|
|
21
|
+
await program.onStart!(start({ text: " Ask about the budget " }), context);
|
|
22
|
+
assert.deepEqual(calls.map((call) => [call.name, call.input]), [
|
|
23
|
+
["actor_program_ensure", { name: "notebook" }],
|
|
24
|
+
["actor_input", { actor: "@notebook", content: "Ask about the budget" }],
|
|
25
|
+
["canvas_layout_place", { entity: "app:notebook/main" }],
|
|
26
|
+
]);
|
|
27
|
+
assert.deepEqual(context.finished, [{ result: { note: "Ask about the budget", notebook: "installed" }, summary: "Noted: Ask about the budget" }]);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("without a start value it notes when it started; an invalid value changes nothing", async () => {
|
|
31
|
+
const { calls, context } = note("active");
|
|
32
|
+
await program.onStart!(start(null), context);
|
|
33
|
+
assert.match(String((calls[1]!.input as { content: string }).content), /^Started at /);
|
|
34
|
+
const invalid = note();
|
|
35
|
+
await assert.rejects(async () => program.onStart!(start({ text: "" }), invalid.context), /start value/);
|
|
36
|
+
assert.deepEqual(invalid.calls, []);
|
|
37
|
+
assert.deepEqual(invalid.context.finished, []);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("an ordinary message is refused", async () => {
|
|
41
|
+
const { context } = note();
|
|
42
|
+
await assert.rejects(async () => program.onInput!({ id: "input-1", content: "hello", artifactIds: [], sourceEventIds: [], subscriptionId: null, event: null }, context), /answers only starts/);
|
|
43
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Take stock of the run
|
|
3
|
+
description: "A prepared check shows a run script that also joins a running run. It lists the other participants, notes them in the shared notebook, and reports them back as its result."
|
|
4
|
+
order: 170
|
|
5
|
+
coordinator: false
|
|
6
|
+
embeddable: true
|
|
7
|
+
shared-programs: notebook
|
|
8
|
+
tags: Run scripts, Concept demo, TypeScript actors
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
The TypeScript program reads the participants with `actor_list` and ends each start with
|
|
12
|
+
`context.finish`. The owner reads the summary in the chat; a coordinator that starts it through
|
|
13
|
+
`run_script_start` receives summary and list as a message. Started inside a running run through the
|
|
14
|
+
run menu, `ragents script`, or the coordinator, it changes neither the primary actor nor the tiles,
|
|
15
|
+
and a repeated start reuses the same actor. Started as a new run, it is the run's only participant.
|
|
16
|
+
|
|
17
|
+
It shares the notebook with the quick note script: `shared-programs: notebook` copies the plugin's
|
|
18
|
+
shared package into the run, `actor_program_ensure` makes it active once, and
|
|
19
|
+
`canvas_layout_place` puts its view next to what is arranged.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineActor } from "@ragents/server";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
|
|
4
|
+
const contract = {
|
|
5
|
+
state: Type.Object({ reports: Type.Optional(Type.Number()) }, { additionalProperties: false }),
|
|
6
|
+
functions: {},
|
|
7
|
+
input: { capabilities: ["actor_list", "actor_program_ensure", "actor_input", "canvas_layout_place"] },
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
export default defineActor(contract, {
|
|
11
|
+
functions: {},
|
|
12
|
+
onInput: () => {
|
|
13
|
+
throw new Error("The roster answers only starts; start it again from the run menu or with run_script_start.");
|
|
14
|
+
},
|
|
15
|
+
onStart: async (_start, context) => {
|
|
16
|
+
const actors = await context.functions.actor_list({});
|
|
17
|
+
const others = actors.filter((actor) => actor.kind !== "human" && actor.handle !== context.actor.handle);
|
|
18
|
+
const handles = others.map((actor) => `@${actor.handle}`);
|
|
19
|
+
const summary = handles.length === 0 ? "No other participants yet." : `${handles.length} participant${handles.length === 1 ? "" : "s"}: ${handles.join(", ")}.`;
|
|
20
|
+
const notebook = await context.functions.actor_program_ensure({ name: "notebook" });
|
|
21
|
+
await context.functions.actor_input({ actor: `@${notebook.handle}`, content: `Roster: ${summary}` });
|
|
22
|
+
await context.functions.canvas_layout_place({ entity: "app:notebook/main" });
|
|
23
|
+
context.state.replace({ reports: (context.state.read().reports ?? 0) + 1 });
|
|
24
|
+
context.finish({ actors: others.map(({ handle, kind, lifecycle }) => ({ handle, kind, lifecycle })) }, { summary });
|
|
25
|
+
},
|
|
26
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { createTestContext } from "@ragents/server/testing";
|
|
4
|
+
import program from "../src/server.ts";
|
|
5
|
+
|
|
6
|
+
const start = { input: null, options: {}, embedded: true, startedBy: "owner-id", count: 1 };
|
|
7
|
+
const actor = (handle: string, kind: "human" | "agent" | "script", lifecycle = "idle") =>
|
|
8
|
+
({ id: `id-${handle}`, handle, displayName: handle, kind, lifecycle, createdBy: null, description: null, toolCount: null });
|
|
9
|
+
|
|
10
|
+
const roster = (actors: ReturnType<typeof actor>[], state: { reports?: number } = {}) => {
|
|
11
|
+
const calls: { name: string; input: unknown }[] = [];
|
|
12
|
+
const record = <T>(name: string, answer: T) => (input: unknown): T => { calls.push({ name, input }); return answer; };
|
|
13
|
+
const context = createTestContext<{ reports?: number }>({ state, functions: {
|
|
14
|
+
actor_list: record("actor_list", actors),
|
|
15
|
+
actor_program_ensure: record("actor_program_ensure", { actorId: "id-notebook", handle: "notebook", status: "active" as const }),
|
|
16
|
+
actor_input: record("actor_input", []),
|
|
17
|
+
canvas_layout_place: record("canvas_layout_place", { placed: true }),
|
|
18
|
+
} });
|
|
19
|
+
return { calls, context };
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
test("reports the other participants as result and summary and notes them in the shared notebook", async () => {
|
|
23
|
+
const { calls, context } = roster([actor("owner", "human"), actor("coordinator", "agent", "running"), actor("helper", "agent"), actor("test", "script")]);
|
|
24
|
+
await program.onStart!(start, context);
|
|
25
|
+
assert.deepEqual(context.finished, [{
|
|
26
|
+
result: { actors: [{ handle: "coordinator", kind: "agent", lifecycle: "running" }, { handle: "helper", kind: "agent", lifecycle: "idle" }] },
|
|
27
|
+
summary: "2 participants: @coordinator, @helper.",
|
|
28
|
+
}]);
|
|
29
|
+
assert.deepEqual(calls.slice(1).map((call) => [call.name, call.input]), [
|
|
30
|
+
["actor_program_ensure", { name: "notebook" }],
|
|
31
|
+
["actor_input", { actor: "@notebook", content: "Roster: 2 participants: @coordinator, @helper." }],
|
|
32
|
+
["canvas_layout_place", { entity: "app:notebook/main" }],
|
|
33
|
+
]);
|
|
34
|
+
assert.deepEqual(context.state.read(), { reports: 1 });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("a run without other participants still gets a result, and every start counts", async () => {
|
|
38
|
+
const { context } = roster([actor("owner", "human"), actor("test", "script")], { reports: 2 });
|
|
39
|
+
await program.onStart!({ ...start, embedded: false, count: 3 }, context);
|
|
40
|
+
assert.deepEqual(context.finished, [{ result: { actors: [] }, summary: "No other participants yet." }]);
|
|
41
|
+
assert.deepEqual(context.state.read(), { reports: 3 });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("an ordinary message is refused", async () => {
|
|
45
|
+
const { context } = roster([]);
|
|
46
|
+
await assert.rejects(async () => program.onInput!({ id: "input-1", content: "hello", artifactIds: [], sourceEventIds: [], subscriptionId: null, event: null }, context), /answers only starts/);
|
|
47
|
+
});
|
|
@@ -47,6 +47,11 @@ export const usage = (): string => `Usage: ragents <command> [arguments]
|
|
|
47
47
|
stop <run> --run [--profile <p>]
|
|
48
48
|
Emergency stop: cancel all turns and stop all actors of the run
|
|
49
49
|
stop --host [--profile <p>] stop the remembered host
|
|
50
|
+
script <run> [--profile <p>] [--json]
|
|
51
|
+
List the run scripts of the profile and whether each can start in the running run.
|
|
52
|
+
script <run> <entry> [--input <json>] [--profile <p>] [--json]
|
|
53
|
+
Start the run script inside the run, as its owner; prints its actor and which start of it
|
|
54
|
+
this is. Only a script whose RUN.md sets embeddable: true joins a running run.
|
|
50
55
|
plugin build <folder...> [--out <o>] [--watch] [--no-typecheck]
|
|
51
56
|
Build plugin source folders into bundles; details with plugin --help.
|
|
52
57
|
|
|
@@ -67,12 +72,14 @@ export type AgentCommand =
|
|
|
67
72
|
| { readonly kind: "journal"; readonly profile: string; readonly runId: string; readonly json: boolean; readonly tools: boolean }
|
|
68
73
|
| { readonly kind: "stop"; readonly profile: string; readonly runId: string }
|
|
69
74
|
| { readonly kind: "stop-run"; readonly profile: string; readonly runId: string }
|
|
70
|
-
| { readonly kind: "stop-host"; readonly profile: string }
|
|
75
|
+
| { readonly kind: "stop-host"; readonly profile: string }
|
|
76
|
+
| { readonly kind: "script"; readonly profile: string; readonly runId: string; readonly entry: string | undefined; readonly input: unknown; readonly json: boolean };
|
|
71
77
|
|
|
72
78
|
interface Flags {
|
|
73
79
|
readonly profile: string;
|
|
74
80
|
readonly entry: string | undefined;
|
|
75
81
|
readonly workstation: string | undefined;
|
|
82
|
+
readonly input: string | undefined;
|
|
76
83
|
readonly json: boolean;
|
|
77
84
|
readonly tools: boolean;
|
|
78
85
|
readonly host: boolean;
|
|
@@ -80,7 +87,7 @@ interface Flags {
|
|
|
80
87
|
readonly positional: readonly string[];
|
|
81
88
|
}
|
|
82
89
|
|
|
83
|
-
const VALUE_FLAGS = new Set(["--profile", "--entry", "--workstation"]);
|
|
90
|
+
const VALUE_FLAGS = new Set(["--profile", "--entry", "--workstation", "--input"]);
|
|
84
91
|
|
|
85
92
|
const scan = (argv: readonly string[], allowed: readonly string[]): Flags => {
|
|
86
93
|
const positional: string[] = [];
|
|
@@ -106,6 +113,7 @@ const scan = (argv: readonly string[], allowed: readonly string[]): Flags => {
|
|
|
106
113
|
profile: values.get("--profile") ?? defaultProfile(),
|
|
107
114
|
entry: values.get("--entry"),
|
|
108
115
|
workstation: values.get("--workstation"),
|
|
116
|
+
input: values.get("--input"),
|
|
109
117
|
json: switches.has("--json"),
|
|
110
118
|
tools: switches.has("--tools"),
|
|
111
119
|
host: switches.has("--host"),
|
|
@@ -158,9 +166,26 @@ export const parseArguments = (argv: readonly string[]): AgentCommand => {
|
|
|
158
166
|
if (!RUN_ID_PATTERN.test(runId)) throw new Error(`Invalid run id: ${runId}`);
|
|
159
167
|
return { kind: flags.run ? "stop-run" : "stop", profile: flags.profile, runId };
|
|
160
168
|
}
|
|
169
|
+
if (command === "script") {
|
|
170
|
+
const flags = scan(rest, ["--profile", "--input", "--json"]);
|
|
171
|
+
const [runId, entry, ...extra] = flags.positional;
|
|
172
|
+
if (!runId) throw new Error(`script needs <run>, optionally followed by <entry>.\n\n${usage()}`);
|
|
173
|
+
if (extra.length > 0) throw new Error(`script takes at most two values, not ${flags.positional.length}.`);
|
|
174
|
+
if (!RUN_ID_PATTERN.test(runId)) throw new Error(`Invalid run id: ${runId}`);
|
|
175
|
+
if (flags.input !== undefined && entry === undefined) throw new Error("--input needs <entry>, the run script it starts.");
|
|
176
|
+
return { kind: "script", profile: flags.profile, runId, entry, input: flags.input === undefined ? null : parsedInput(flags.input), json: flags.json };
|
|
177
|
+
}
|
|
161
178
|
throw new Error(command ? `Unknown command: ${command}\n\n${usage()}` : usage());
|
|
162
179
|
};
|
|
163
180
|
|
|
181
|
+
const parsedInput = (text: string): unknown => {
|
|
182
|
+
try {
|
|
183
|
+
return JSON.parse(text);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
throw new Error(`--input is not JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
164
189
|
export const loadProfile = (selection: string, root = hostRoot()): Promise<ProfileTarget> => selectProfileTarget(selection, root);
|
|
165
190
|
|
|
166
191
|
const delay = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -544,6 +569,24 @@ const stopCommand = async (command: Extract<AgentCommand, { kind: "stop" | "stop
|
|
|
544
569
|
return 0;
|
|
545
570
|
};
|
|
546
571
|
|
|
572
|
+
const scriptCommand = async (command: Extract<AgentCommand, { kind: "script" }>, write: LineWriter): Promise<number> => {
|
|
573
|
+
const target = await loadProfile(command.profile);
|
|
574
|
+
const baseUrl = await addressOf(target);
|
|
575
|
+
if (!await healthy(baseUrl)) throw new Error(`No RAgents server responds at ${baseUrl}; start it with ragents run.`);
|
|
576
|
+
const rpc = client(baseUrl);
|
|
577
|
+
if (command.entry === undefined) {
|
|
578
|
+
const scripts = await withLoginHint(() => rpc.call(coreContracts.runs.scripts, { runId: command.runId }));
|
|
579
|
+
for (const script of scripts) {
|
|
580
|
+
write(command.json ? JSON.stringify(script) : `${script.id} ${script.title}${script.available ? "" : ` (not available: ${script.reason ?? "no reason given"})`}`);
|
|
581
|
+
}
|
|
582
|
+
return 0;
|
|
583
|
+
}
|
|
584
|
+
const started = await withLoginHint(() => rpc.call(coreContracts.runs.startScript, { runId: command.runId, entry: command.entry!, input: command.input }));
|
|
585
|
+
write(command.json ? JSON.stringify(started) : `script: @${started.handle}, start ${started.count}`);
|
|
586
|
+
write(`run: ${command.runId}`);
|
|
587
|
+
return 0;
|
|
588
|
+
};
|
|
589
|
+
|
|
547
590
|
const alive = (pid: number): boolean => {
|
|
548
591
|
try {
|
|
549
592
|
process.kill(pid, 0);
|
|
@@ -596,6 +639,7 @@ export const execute = async (command: AgentCommand, write: LineWriter = toStdou
|
|
|
596
639
|
if (command.kind === "send") return sendCommand(command, write);
|
|
597
640
|
if (command.kind === "journal") return journalCommand(command, write);
|
|
598
641
|
if (command.kind === "stop" || command.kind === "stop-run") return stopCommand(command);
|
|
642
|
+
if (command.kind === "script") return scriptCommand(command, write);
|
|
599
643
|
return stopHostCommand(command);
|
|
600
644
|
};
|
|
601
645
|
|
|
@@ -17,6 +17,7 @@ export const commands = {
|
|
|
17
17
|
send: AGENT_CLI,
|
|
18
18
|
journal: AGENT_CLI,
|
|
19
19
|
stop: AGENT_CLI,
|
|
20
|
+
script: AGENT_CLI,
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
const usage = `Usage: ragents <command> [arguments]
|
|
@@ -25,6 +26,7 @@ const usage = `Usage: ragents <command> [arguments]
|
|
|
25
26
|
send <run> "<text>" follow-up task in the same run
|
|
26
27
|
journal <run> [--tools] read the history of a run
|
|
27
28
|
stop <run> | stop --host cancel the running turn or stop the remembered host
|
|
29
|
+
script <run> [<entry>] list the run scripts or start one inside the run (--input <json>)
|
|
28
30
|
connect <server-url> fetch the client profile with its bundles and start the local server with it
|
|
29
31
|
start <profile|path> start a profile of the package, an own profile file or a
|
|
30
32
|
fetched revision; the UI comes prebuilt
|
|
@@ -33,7 +35,7 @@ const usage = `Usage: ragents <command> [arguments]
|
|
|
33
35
|
plugin build <folder...> build plugin source folders into bundles (--out, --watch)
|
|
34
36
|
--help, help this usage
|
|
35
37
|
|
|
36
|
-
run, send, journal and
|
|
38
|
+
run, send, journal, stop and script take another profile with --profile <profile|path>: a name
|
|
37
39
|
next to the host or the path to an own ragents.config.<profile>.ts. RAGENTS_PROFILE sets
|
|
38
40
|
the same for all commands of a shell; if the profile requires sign-in, the user's
|
|
39
41
|
personal token goes into RAGENTS_TOKEN.
|