@divaltor/academy 0.3.3 → 0.4.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 CHANGED
@@ -30,8 +30,10 @@ Add Academy to `opencode.jsonc`. All options are optional:
30
30
  "model": "anthropic/claude-opus-4-1#max",
31
31
  },
32
32
  },
33
- "communication": {
34
- "thread_summary": "opencode-go/glm-5.3-flash",
33
+ "experimental": {
34
+ "communication": {
35
+ "thread_summary": "opencode-go/glm-5.3-flash",
36
+ },
35
37
  },
36
38
  "use_fff": true,
37
39
  },
@@ -56,15 +58,15 @@ Each agent under `agents` accepts `name`, `color`, and `model`. Colors may be si
56
58
  >
57
59
  > An agent's name is also its session ID. Renaming it can prevent existing sessions from continuing.
58
60
 
59
- Session tools are available under Code Mode's `academy` namespace. Only the root primary session can create threads; omit `agent` for a general coding session.
61
+ Session tools are experimental and off by default; set `experimental.communication` to enable them. They are available under Code Mode's `academy` namespace. Only the root primary session can create threads; omit `agent` for a general coding session.
60
62
 
61
63
  > [!NOTE]
62
64
  >
63
- > `read_thread` with a question summarizes via the `communication.thread_summary` model, or the calling session's model when unset. Models on `opencode-go` cannot use stateless generation (the provider requires session routing headers that only a session request carries), so Academy answers through a dedicated `Academy summary <model>` session via transient `session.generate` — no transcript writes, but the session stays visible in the session list because the plugin API exposes neither archive nor delete for it.
65
+ > `read_thread` with a question summarizes via the `experimental.communication.thread_summary` model, or the calling session's model when unset. Models on `opencode-go` cannot use stateless generation (the provider requires session routing headers that only a session request carries), so Academy answers through a dedicated `Academy summary <model>` session via transient `session.generate` — no transcript writes, but the session stays visible in the session list because the plugin API exposes neither archive nor delete for it.
64
66
 
65
67
  > [!NOTE]
66
68
  >
67
- > `find_thread` currently searches Academy-created sessions only. OpenCode's HTTP client can list persisted sessions, but the plugin context does not yet expose `ctx.session.list`, so Academy cannot safely discover sessions created before the plugin was installed. We are waiting for upstream support rather than reading OpenCode's private SQLite database or connecting back to the server through a separate client.
69
+ > `find_thread` currently searches Academy-created sessions only and is part of the experimental communication tools. OpenCode's HTTP client can list persisted sessions, but the plugin context does not yet expose `ctx.session.list`, so Academy cannot safely discover sessions created before the plugin was installed. We are waiting for upstream support rather than reading OpenCode's private SQLite database or connecting back to the server through a separate client.
68
70
  >
69
71
  > Tracking: [#43517](https://github.com/anomalyco/opencode/issues/43517), [#44155](https://github.com/anomalyco/opencode/issues/44155), [#43556](https://github.com/anomalyco/opencode/pull/43556), and [#46690](https://github.com/anomalyco/opencode/pull/46690).
70
72
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@divaltor/academy",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "description": "An OpenCode plugin that trains a Tracen Academy roster of specialist coding agents to research, build, and review.",
5
5
  "homepage": "https://github.com/divaltor/academy#readme",
6
6
  "bugs": {
@@ -0,0 +1,6 @@
1
+ ## Session collaboration
2
+
3
+ - Academy session tools are available through Code Mode's `academy` namespace. Use Code Mode search when an exact signature is not listed and asynchronous or resumable work would materially help.
4
+ - Only Rudolf creates and controls these sessions. Use native subagents for bounded work whose result is needed immediately; use an Academy thread for independently specifiable work that can proceed concurrently or may need follow-up later. Never delegate the same work through both paths.
5
+ - Give each thread a complete prompt and non-overlapping ownership because it does not inherit this conversation. Retain the returned session ID, continue useful work, and wait only when its result becomes necessary.
6
+ - Use `find_thread` to recover prior Academy work, `send_thread_message` for corrections or added context, and `interrupt_thread` only for obsolete or wrong-premise work. Read and verify the result before synthesizing it for the user.
@@ -29,13 +29,6 @@ You are **Rudolf**, the primary coding agent. Work directly by default and deliv
29
29
  - Give delegates the goal, relevant context, constraints, non-goals, and required result. Parallelize only independent work and never assign overlapping writes.
30
30
  - Verify consequential delegated claims and local fit, inspect delegated changes, and synthesize the user-facing result yourself.
31
31
 
32
- ## Session collaboration
33
-
34
- - Academy session tools are available through Code Mode's `academy` namespace. Use Code Mode search when an exact signature is not listed and asynchronous or resumable work would materially help.
35
- - Only Rudolf creates and controls these sessions. Use native subagents for bounded work whose result is needed immediately; use an Academy thread for independently specifiable work that can proceed concurrently or may need follow-up later. Never delegate the same work through both paths.
36
- - Give each thread a complete prompt and non-overlapping ownership because it does not inherit this conversation. Retain the returned session ID, continue useful work, and wait only when its result becomes necessary.
37
- - Use `find_thread` to recover prior Academy work, `send_thread_message` for corrections or added context, and `interrupt_thread` only for obsolete or wrong-premise work. Read and verify the result before synthesizing it for the user.
38
-
39
32
  # Communication
40
33
 
41
34
  - Default to roughly ten lines of prose per answer. Add structure only when it carries information.
package/src/agents.ts CHANGED
@@ -4,6 +4,8 @@ import cafeSystem from "./agents/cafe.txt" with { type: "text" };
4
4
  import dantsuSystem from "./agents/dantsu.txt" with { type: "text" };
5
5
  import rudolfSystem from "./agents/rudolf.txt" with { type: "text" };
6
6
 
7
+ export { default as rudolfCommunicationPrompt } from "./agents/rudolf-communication.txt" with { type: "text" };
8
+
7
9
  interface Permission {
8
10
  readonly action: string;
9
11
  readonly resource: string;
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Agent, Model, Plugin } from "@opencode/plugin/effect";
2
2
  import { Config, Effect, Option, Redacted, Schema } from "effect";
3
3
 
4
- import { agents } from "./agents";
4
+ import { agents, rudolfCommunicationPrompt } from "./agents";
5
5
  import { FffTools } from "./fff-tools";
6
6
  import { SessionCommunication } from "./session-communication";
7
7
 
@@ -26,9 +26,13 @@ const AgentsOptions = Schema.Struct({
26
26
  rudolf: Schema.optionalKey(AgentOptions),
27
27
  });
28
28
 
29
+ const ExperimentalOptions = Schema.Struct({
30
+ communication: Schema.optionalKey(SessionCommunication.Options),
31
+ });
32
+
29
33
  const AcademyOptions = Schema.Struct({
30
34
  agents: Schema.optionalKey(AgentsOptions),
31
- communication: Schema.optionalKey(SessionCommunication.Options),
35
+ experimental: Schema.optionalKey(ExperimentalOptions),
32
36
  use_fff: Schema.optionalKey(Schema.Boolean),
33
37
  });
34
38
 
@@ -49,6 +53,7 @@ const setup = Effect.fn("Academy.setup")(function* setup(ctx: Plugin.Context) {
49
53
  const agentID = (id: (typeof agents)[number]["id"]) =>
50
54
  Agent.ID.make(agentOptions[id]?.name ?? id);
51
55
  const configuredAgentIDs = agents.map((agent) => agentID(agent.id));
56
+ const communication = options.experimental?.communication;
52
57
 
53
58
  if (new Set(configuredAgentIDs).size !== configuredAgentIDs.length) {
54
59
  return yield* Effect.die(
@@ -96,7 +101,14 @@ const setup = Effect.fn("Academy.setup")(function* setup(ctx: Plugin.Context) {
96
101
  agent.mode = definition.mode;
97
102
  agent.color =
98
103
  agentOptions[definition.id]?.color ?? agent.color ?? definition.color;
99
- agent.system = definition.system.replaceAll(
104
+ const base =
105
+ definition.id === "rudolf" && communication !== undefined
106
+ ? definition.system.replace(
107
+ "# Communication",
108
+ `${rudolfCommunicationPrompt}\n\n# Communication`
109
+ )
110
+ : definition.system;
111
+ agent.system = base.replaceAll(
100
112
  /\b(?:Rudolf|Agnes|Cafe|Dantsu|Bellno)\b/gu,
101
113
  (name) => agentNames.get(name) ?? name
102
114
  );
@@ -121,11 +133,9 @@ const setup = Effect.fn("Academy.setup")(function* setup(ctx: Plugin.Context) {
121
133
  editor.default(agentID("rudolf"));
122
134
  });
123
135
 
124
- yield* SessionCommunication.register(
125
- ctx,
126
- options.communication ?? {},
127
- agentID("rudolf")
128
- );
136
+ if (communication !== undefined) {
137
+ yield* SessionCommunication.register(ctx, communication, agentID("rudolf"));
138
+ }
129
139
  });
130
140
 
131
141
  export default Plugin.define({