@cortexkit/aft-opencode 0.58.2 → 0.60.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/dist/bash-wait-detach.d.ts.map +1 -1
- package/dist/bg-notifications.d.ts +13 -0
- package/dist/bg-notifications.d.ts.map +1 -1
- package/dist/bridge-bootstrap.d.ts +6 -6
- package/dist/bridge-bootstrap.d.ts.map +1 -1
- package/dist/config-live-reload.d.ts +36 -8
- package/dist/config-live-reload.d.ts.map +1 -1
- package/dist/config.d.ts +72 -17
- package/dist/config.d.ts.map +1 -1
- package/dist/configure-warnings.d.ts.map +1 -1
- package/dist/entry/server.js +5079 -2255
- package/dist/hooks/auto-update-checker/checker.d.ts.map +1 -1
- package/dist/hooks/auto-update-checker/index.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4674 -1999
- package/dist/lsp-auto-install.d.ts.map +1 -1
- package/dist/lsp-github-install.d.ts +11 -2
- package/dist/lsp-github-install.d.ts.map +1 -1
- package/dist/notifications.d.ts +1 -1
- package/dist/notifications.d.ts.map +1 -1
- package/dist/rpc/contract.d.ts +1 -1
- package/dist/rpc/contract.d.ts.map +1 -1
- package/dist/rpc/register.d.ts +3 -1
- package/dist/rpc/register.d.ts.map +1 -1
- package/dist/shared/ignored-message.d.ts.map +1 -1
- package/dist/shared/read-vision.d.ts.map +1 -1
- package/dist/shared/rpc-client.d.ts +8 -0
- package/dist/shared/rpc-client.d.ts.map +1 -1
- package/dist/shared/rpc-server.d.ts.map +1 -1
- package/dist/shared/session-directory.d.ts +7 -5
- package/dist/shared/session-directory.d.ts.map +1 -1
- package/dist/shared/status.d.ts +10 -4
- package/dist/shared/status.d.ts.map +1 -1
- package/dist/shared/v2-context.d.ts +3 -0
- package/dist/shared/v2-context.d.ts.map +1 -0
- package/dist/subc-tool-schemas.d.ts +23 -1
- package/dist/subc-tool-schemas.d.ts.map +1 -1
- package/dist/tool-registration.d.ts +6 -3
- package/dist/tool-registration.d.ts.map +1 -1
- package/dist/tools/_shared.d.ts +2 -5
- package/dist/tools/_shared.d.ts.map +1 -1
- package/dist/tools/bash.d.ts +17 -0
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash_watch.d.ts +11 -1
- package/dist/tools/bash_watch.d.ts.map +1 -1
- package/dist/tools/definitions/v2.d.ts +11 -1
- package/dist/tools/definitions/v2.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts +18 -0
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/inspect.d.ts +10 -1
- package/dist/tools/inspect.d.ts.map +1 -1
- package/dist/tools/permissions.d.ts +2 -1
- package/dist/tools/permissions.d.ts.map +1 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/semantic.d.ts.map +1 -1
- package/dist/tui/aft-rpc.d.ts +14 -0
- package/dist/tui/aft-rpc.d.ts.map +1 -1
- package/dist/tui/v2-status.d.ts.map +1 -1
- package/dist/types.d.ts +7 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/v2-host-tool-names.d.ts +43 -0
- package/dist/v2-host-tool-names.d.ts.map +1 -0
- package/dist/v2-prompt-detach.d.ts +1 -0
- package/dist/v2-prompt-detach.d.ts.map +1 -1
- package/dist/v2-session-lifecycle.d.ts +22 -0
- package/dist/v2-session-lifecycle.d.ts.map +1 -0
- package/dist/v2-tool-hooks.d.ts +18 -0
- package/dist/v2-tool-hooks.d.ts.map +1 -0
- package/dist/v2-workflow-hints.d.ts +4 -0
- package/dist/v2-workflow-hints.d.ts.map +1 -0
- package/dist/wakes/runtime-consumer.d.ts +6 -1
- package/dist/wakes/runtime-consumer.d.ts.map +1 -1
- package/dist/wakes/session-delivery.d.ts +12 -7
- package/dist/wakes/session-delivery.d.ts.map +1 -1
- package/package.json +9 -9
- package/src/entry/server-runtime.mjs +36 -4
- package/src/shared/ignored-message.ts +18 -0
- package/src/shared/read-vision.ts +14 -4
- package/src/shared/rpc-client.ts +46 -5
- package/src/shared/rpc-server.ts +10 -0
- package/src/shared/session-directory.ts +67 -19
- package/src/shared/status.ts +40 -9
- package/src/shared/v2-context.ts +7 -0
- package/src/tui/aft-rpc.ts +11 -0
- package/src/tui/index.tsx +20 -16
- package/src/tui/sidebar-view.tsx +9 -1
- package/src/tui/v2-status.ts +8 -5
- package/src/tui/v2.tsx +50 -0
- package/src/tui-compiled/aft-rpc.ts +11 -0
- package/src/tui-compiled/index.tsx +27 -16
- package/src/tui-compiled/sidebar-view.tsx +11 -2
- package/src/tui-compiled/v2-status.ts +8 -5
- package/src/tui-compiled/v2.tsx +29 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAc,MAAM,qBAAqB,CAAC;AAEtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAyGjD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CA0BrD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAoL9E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semantic.d.ts","sourceRoot":"","sources":["../../src/tools/semantic.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAoGjD,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"semantic.d.ts","sourceRoot":"","sources":["../../src/tools/semantic.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAoGjD,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAsKhF"}
|
package/dist/tui/aft-rpc.d.ts
CHANGED
|
@@ -10,8 +10,17 @@ export declare const IndexProgress: Schema.Struct<{
|
|
|
10
10
|
readonly completed: Schema.optional<Schema.Number>;
|
|
11
11
|
readonly total: Schema.optional<Schema.Number>;
|
|
12
12
|
}>;
|
|
13
|
+
/**
|
|
14
|
+
* A one-off notice for the user, shown by the OpenCode 2 TUI as a toast. It
|
|
15
|
+
* travels over RPC rather than into the session because anything written into
|
|
16
|
+
* a session (a synthetic record included) is sent to the model on later turns.
|
|
17
|
+
*/
|
|
18
|
+
export declare const HostToolOverlapNotice: Schema.Struct<{
|
|
19
|
+
readonly message: Schema.String;
|
|
20
|
+
}>;
|
|
13
21
|
export type AftRpcSession = typeof RpcSession.Type;
|
|
14
22
|
export type AftIndexProgress = typeof IndexProgress.Type;
|
|
23
|
+
export type AftHostToolOverlapNotice = typeof HostToolOverlapNotice.Type;
|
|
15
24
|
export declare const AftRpc: {
|
|
16
25
|
readonly id: "aft";
|
|
17
26
|
readonly methods: {
|
|
@@ -43,6 +52,11 @@ export declare const AftRpc: {
|
|
|
43
52
|
readonly total: Schema.optional<Schema.Number>;
|
|
44
53
|
}>;
|
|
45
54
|
};
|
|
55
|
+
readonly hostToolOverlap: {
|
|
56
|
+
readonly schema: Schema.Struct<{
|
|
57
|
+
readonly message: Schema.String;
|
|
58
|
+
}>;
|
|
59
|
+
};
|
|
46
60
|
};
|
|
47
61
|
};
|
|
48
62
|
//# sourceMappingURL=aft-rpc.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aft-rpc.d.ts","sourceRoot":"","sources":["../../src/tui/aft-rpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAahC,eAAO,MAAM,UAAU;;EAErB,CAAC;AAEH,eAAO,MAAM,aAAa;;;;;;;EAOxB,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,UAAU,CAAC,IAAI,CAAC;AACnD,MAAM,MAAM,gBAAgB,GAAG,OAAO,aAAa,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"aft-rpc.d.ts","sourceRoot":"","sources":["../../src/tui/aft-rpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAahC,eAAO,MAAM,UAAU;;EAErB,CAAC;AAEH,eAAO,MAAM,aAAa;;;;;;;EAOxB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,qBAAqB;;EAEhC,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,UAAU,CAAC,IAAI,CAAC;AACnD,MAAM,MAAM,gBAAgB,GAAG,OAAO,aAAa,CAAC,IAAI,CAAC;AACzD,MAAM,MAAM,wBAAwB,GAAG,OAAO,qBAAqB,CAAC,IAAI,CAAC;AAEzE,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAcjB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v2-status.d.ts","sourceRoot":"","sources":["../../src/tui/v2-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAwC,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"v2-status.d.ts","sourceRoot":"","sources":["../../src/tui/v2-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAwC,MAAM,kBAAkB,CAAC;AAyBhG;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,GAAG,MAAM,CAY/E;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAWF;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,GAAG,iBAAiB,CAsBvF"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AftTransportPool } from "@cortexkit/aft-bridge";
|
|
1
|
+
import type { AftTransportPool, HostToolNames } from "@cortexkit/aft-bridge";
|
|
2
2
|
import type { PluginInput } from "@opencode-ai/plugin";
|
|
3
3
|
import type { AftConfig } from "./config.js";
|
|
4
4
|
interface ShellEnvPluginHost {
|
|
@@ -25,6 +25,12 @@ export interface PluginContext {
|
|
|
25
25
|
config: AftConfig;
|
|
26
26
|
/** Whether hashline edit/read mode is active for this plugin registration. */
|
|
27
27
|
hashlineEffective?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* What this host calls AFT's command and patch tools, sent on every request
|
|
30
|
+
* so AFT's reply text names them. Set by the OpenCode 2 runtime only; absent
|
|
31
|
+
* leaves AFT's own names.
|
|
32
|
+
*/
|
|
33
|
+
hostToolNames?: HostToolNames;
|
|
28
34
|
/** Absolute path to AFT's storage dir (e.g. ~/.local/share/opencode/storage/plugin/aft) */
|
|
29
35
|
storageDir: string;
|
|
30
36
|
/** Returns false when user and project config merge to `enabled: false` for this project. */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,UAAU,kBAAkB;IAC1B,OAAO,CAAC,EAAE,CACR,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,EAC7D,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,KACnC,OAAO,CAAC;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC,GAAG;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;CACnF;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAC;IACnB,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC;CACrD"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The names AFT registers its command and patch tools under on OpenCode 2.
|
|
3
|
+
*
|
|
4
|
+
* OpenCode 2's timeline gives its rich rows (title, command and output for a
|
|
5
|
+
* command; file count and diff for a patch) only to tools it recognises by
|
|
6
|
+
* name, and those names are `shell` and `patch`. OpenCode 2 also files shell
|
|
7
|
+
* permission rules under `shell`. So on OpenCode 2 AFT's `bash` is registered
|
|
8
|
+
* as `shell` and its `apply_patch` as `patch`, replacing the host's built-ins
|
|
9
|
+
* under the same names. OpenCode 1 and Pi keep `bash` and `apply_patch`.
|
|
10
|
+
*
|
|
11
|
+
* Only the name the host and the model see changes. Inside the plugin the
|
|
12
|
+
* tool map, `disabled_tools`, the config keys and every call into AFT's engine
|
|
13
|
+
* keep the AFT names; this module translates at the host boundary in both
|
|
14
|
+
* directions. The engine gets the same map on every request (the
|
|
15
|
+
* `host_tool_names` field beside the session), so the text it renders names
|
|
16
|
+
* the tools the model can actually call.
|
|
17
|
+
*/
|
|
18
|
+
/** AFT tool name -> the name it is registered under on OpenCode 2. */
|
|
19
|
+
export declare const V2_HOST_TOOL_NAMES: Readonly<Record<"bash" | "apply_patch", string>>;
|
|
20
|
+
/** The name an AFT tool is registered under on OpenCode 2. */
|
|
21
|
+
export declare function v2HostToolName(aftName: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* The AFT tool a host-side tool name refers to, for hook events the host keys
|
|
24
|
+
* by the registered name. A name AFT never renamed comes back unchanged.
|
|
25
|
+
*/
|
|
26
|
+
export declare function aftToolNameForV2Host(hostName: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Rewrite the references to `bash` and `apply_patch` as tools in a description
|
|
29
|
+
* or hint written for OpenCode 1, so it names the tools an OpenCode 2 model
|
|
30
|
+
* can call.
|
|
31
|
+
*
|
|
32
|
+
* The shared descriptions are built once for both hosts, and on OpenCode 1
|
|
33
|
+
* they must stay byte-identical (a changed byte rebuilds every session's
|
|
34
|
+
* prompt cache), so they are rewritten here instead of being parameterised at
|
|
35
|
+
* every call site. Only a tool reference changes:
|
|
36
|
+
* - `bash(` and `` `bash` `` and a bare word `bash` become `shell`;
|
|
37
|
+
* - `` `apply_patch` `` and `apply_patch(` become `patch`.
|
|
38
|
+
* These stay: config keys (`bash.background`), companion tools (`bash_status`),
|
|
39
|
+
* task ids (`bash-6b45…`), and `bash` as a program or a syntax ("python/node/
|
|
40
|
+
* bash REPLs", "bash itself", "bash syntax").
|
|
41
|
+
*/
|
|
42
|
+
export declare function renameV2ToolReferences(text: string): string;
|
|
43
|
+
//# sourceMappingURL=v2-host-tool-names.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v2-host-tool-names.d.ts","sourceRoot":"","sources":["../src/v2-host-tool-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,GAAG,aAAa,EAAE,MAAM,CAAC,CAG9E,CAAC;AAMH,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI3D"}
|
|
@@ -35,6 +35,7 @@ export interface V2PromptDetachRuntime {
|
|
|
35
35
|
* `bash.detach_on_user_message` applies to the next message.
|
|
36
36
|
*/
|
|
37
37
|
getConfig(): AftConfig;
|
|
38
|
+
registerSession?: (sessionID: string) => void;
|
|
38
39
|
}
|
|
39
40
|
/**
|
|
40
41
|
* Run the shared chat-message decision on one OpenCode 2 prompt and write the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v2-prompt-detach.d.ts","sourceRoot":"","sources":["../src/v2-prompt-detach.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"v2-prompt-detach.d.ts","sourceRoot":"","sources":["../src/v2-prompt-detach.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,gCAAgC,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,8DAA8D;AAC9D,UAAU,aAAa;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,cAAc;IACtB,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,0FAA0F;AAC1F,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;QACzB,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;QAC1B,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;KAC3B,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,gCAAgC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE9E,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,SAAS,IAAI,SAAS,CAAC;IACvB,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C;AAqCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,qBAAqB,EAC9B,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAuBT;AAMD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,qBAAqB,GAC7B,CAAC,KAAK,EAAE,iBAAiB,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAgBnD;AAOD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,qBAAqB,GAC7B,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAYxC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { PluginContext } from "./types.js";
|
|
3
|
+
/** Hold a racing wake until the host classifies an interrupted tool's execution. */
|
|
4
|
+
export declare function noteV2ToolAbort(client: unknown, sessionID: string): void;
|
|
5
|
+
export declare function awaitV2ExecutionSettlement(client: unknown, sessionID: string): Promise<void>;
|
|
6
|
+
export declare function v2SubagentStopped(client: unknown, sessionID: string): boolean;
|
|
7
|
+
export declare function v2StopEpoch(client: unknown, sessionID: string): number;
|
|
8
|
+
/** Only a command's spawning call records ownership; watching another session's task does not. */
|
|
9
|
+
export declare function trackV2BackgroundTask(ctx: PluginContext, runtime: {
|
|
10
|
+
sessionID: string;
|
|
11
|
+
directory: string;
|
|
12
|
+
}, taskId: string, startedEpoch: number): Promise<void>;
|
|
13
|
+
export declare function allowV2WatchWake(client: unknown, sessionID: string, taskId: string): void;
|
|
14
|
+
export declare function v2WatchWakeAllowed(client: unknown, sessionID: string, taskId: string): boolean;
|
|
15
|
+
export declare function forgetV2Watch(client: unknown, sessionID: string, taskId: string): void;
|
|
16
|
+
export declare function trackV2Watch(ctx: PluginContext, runtime: {
|
|
17
|
+
sessionID: string;
|
|
18
|
+
directory: string;
|
|
19
|
+
}, taskId: string, watchId: unknown): Promise<void>;
|
|
20
|
+
/** Own the lazy host subscription before any tool can register a watch. */
|
|
21
|
+
export declare function registerV2SessionLifecycle(context: unknown, ctx?: PluginContext): Effect.Effect<void, never, import("effect/Scope").Scope>;
|
|
22
|
+
//# sourceMappingURL=v2-session-lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v2-session-lifecycle.d.ts","sourceRoot":"","sources":["../src/v2-session-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AAKxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAWhD,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAQxE;AAED,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAEf;AAqBD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAE7E;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEtE;AAoBD,kGAAkG;AAClG,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EACjD,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAWf;AA0DD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAGzF;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAG9F;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAQtF;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EACjD,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED,2EAA2E;AAC3E,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,aAAa,4DAqF/E"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { PluginContext } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Register the OpenCode 2 hooks that do what AFT's OpenCode 1 hooks do:
|
|
5
|
+
* normalise a call's raw arguments before the host validates them, append the
|
|
6
|
+
* merge-conflict hint to a command's result text, gate the editing tools by
|
|
7
|
+
* model and remember the session's current model.
|
|
8
|
+
*
|
|
9
|
+
* `registeredTools` holds AFT tool names; the host's events name tools by the
|
|
10
|
+
* name they are registered under, which differs for `bash` (`shell`) and
|
|
11
|
+
* `apply_patch` (`patch`). `bashDescription` is the description AFT's bash
|
|
12
|
+
* tool was built with, as written for OpenCode 1.
|
|
13
|
+
*/
|
|
14
|
+
export declare function registerV2ToolHooks(context: unknown, runtime: {
|
|
15
|
+
hashlineEffective?: boolean;
|
|
16
|
+
pool?: PluginContext["pool"];
|
|
17
|
+
}, registeredTools: ReadonlySet<string>, bashDescription?: string): Effect.Effect<void, never, import("effect/Scope").Scope>;
|
|
18
|
+
//# sourceMappingURL=v2-tool-hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v2-tool-hooks.d.ts","sourceRoot":"","sources":["../src/v2-tool-hooks.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AAKxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAsDhD;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE;IAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,EACtE,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,EACpC,eAAe,CAAC,EAAE,MAAM,4DAgGzB"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
/** V2 consumes session system hooks, not V1's chat.system.transform. */
|
|
3
|
+
export declare function registerV2WorkflowHints(context: unknown, hintsBlock: string | null): Effect.Effect<boolean, never, import("effect/Scope").Scope>;
|
|
4
|
+
//# sourceMappingURL=v2-workflow-hints.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v2-workflow-hints.d.ts","sourceRoot":"","sources":["../src/v2-workflow-hints.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,wEAAwE;AACxE,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,+DAelF"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BridgeOptions } from "@cortexkit/aft-bridge";
|
|
2
|
+
import type { AftConfig } from "../config.js";
|
|
2
3
|
import type { V2ToolConsumers } from "../tools/definitions/v2.js";
|
|
3
4
|
import { type V2SessionAdmission } from "./session-delivery.js";
|
|
4
5
|
export interface V2WakeHostContext {
|
|
@@ -15,7 +16,11 @@ export interface V2RuntimeConsumer extends V2ToolConsumers {
|
|
|
15
16
|
* Creates the V2 execution consumers for one OpenCode Location. The bridge owns
|
|
16
17
|
* one process-wide `onBashCompletion` callback, so it routes each notification
|
|
17
18
|
* by session to the delivery object owned by that Location's Effect scope.
|
|
19
|
+
*
|
|
20
|
+
* `getConfig` returns the Location's current AFT config (a live reload
|
|
21
|
+
* replaces it); it decides whether an admitted completion detaches the
|
|
22
|
+
* session's waits.
|
|
18
23
|
*/
|
|
19
|
-
export declare function createV2RuntimeConsumer(context: V2WakeHostContext): V2RuntimeConsumer;
|
|
24
|
+
export declare function createV2RuntimeConsumer(context: V2WakeHostContext, getConfig?: () => AftConfig): V2RuntimeConsumer;
|
|
20
25
|
export {};
|
|
21
26
|
//# sourceMappingURL=runtime-consumer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-consumer.d.ts","sourceRoot":"","sources":["../../src/wakes/runtime-consumer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,aAAa,EACd,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime-consumer.d.ts","sourceRoot":"","sources":["../../src/wakes/runtime-consumer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,aAAa,EACd,MAAM,uBAAuB,CAAC;AAS/B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAQlE,OAAO,EAAE,KAAK,kBAAkB,EAAqB,MAAM,uBAAuB,CAAC;AAEnF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;CACtC;AAyJD,mFAAmF;AACnF,wBAAgB,2BAA2B,IAAI,IAAI,CAKlD;AAED,QAAA,MAAM,mBAAmB,EAAE,IAAI,CAAC,aAAa,EAAE,kBAAkB,GAAG,mBAAmB,CAGtF,CAAC;AAEF,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,QAAQ,CAAC,aAAa,EAAE,OAAO,mBAAmB,CAAC;IACnD,OAAO,IAAI,IAAI,CAAC;CACjB;AAiCD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,iBAAiB,EAC1B,SAAS,GAAE,MAAM,SAAsB,GACtC,iBAAiB,CAsFnB"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
/**
|
|
3
|
-
* The `metadata.source`
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* tell AFT's machine-written text from text an operator typed, never the text.
|
|
3
|
+
* The `metadata.source` marker for AFT-written session input. Synthetic wakes
|
|
4
|
+
* retain it, and the prompt hook recognises it on machine-originated prompts
|
|
5
|
+
* so they are not treated as text an operator typed.
|
|
7
6
|
*/
|
|
8
7
|
export declare const AFT_PROMPT_SOURCE = "aft";
|
|
9
8
|
/** True when a prompt's metadata marks it as one AFT admitted itself. */
|
|
@@ -20,9 +19,15 @@ export interface V2SessionSyntheticInput {
|
|
|
20
19
|
readonly text: string;
|
|
21
20
|
readonly description?: string;
|
|
22
21
|
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
23
|
-
readonly
|
|
22
|
+
readonly delivery?: "steer" | "queue";
|
|
23
|
+
readonly resume?: boolean;
|
|
24
24
|
}
|
|
25
25
|
export interface V2SessionAdmission {
|
|
26
|
+
get(input: {
|
|
27
|
+
readonly sessionID: string;
|
|
28
|
+
}): Effect.Effect<{
|
|
29
|
+
readonly revert?: unknown;
|
|
30
|
+
}, unknown>;
|
|
26
31
|
prompt(input: V2SessionPromptInput): Effect.Effect<unknown, unknown>;
|
|
27
32
|
synthetic(input: V2SessionSyntheticInput): Effect.Effect<unknown, unknown>;
|
|
28
33
|
}
|
|
@@ -39,8 +44,8 @@ export interface StatusOnlyUpdate {
|
|
|
39
44
|
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
40
45
|
}
|
|
41
46
|
/**
|
|
42
|
-
* Admit V2 session updates
|
|
43
|
-
*
|
|
47
|
+
* Admit V2 session updates as synthetic input so machine-written notices are
|
|
48
|
+
* not rendered as user turns. Completions wake execution; status does not.
|
|
44
49
|
*/
|
|
45
50
|
export declare class V2SessionDelivery {
|
|
46
51
|
private readonly session;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-delivery.d.ts","sourceRoot":"","sources":["../../src/wakes/session-delivery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"session-delivery.d.ts","sourceRoot":"","sources":["../../src/wakes/session-delivery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAQ,MAAM,QAAQ,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAEvC,yEAAyE;AACzE,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAMhE;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC,MAAM,CAAC;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,OAAO,CAAC,CAAC;IAClG,MAAM,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACrE,SAAS,CAAC,KAAK,EAAE,uBAAuB,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACvD;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACvD;AAED;;;GAGG;AACH,qBAAa,iBAAiB;IAIhB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD,OAAO,CAAC,QAAQ,CAAS;gBAEI,OAAO,EAAE,kBAAkB;IAExD,UAAU,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;IAgDlE,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC;IAe7D,OAAO,IAAI,IAAI;CAKhB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/aft-opencode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.60.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenCode plugin for Agent File Tools (AFT) — tree-sitter and lsp powered code analysis",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -33,13 +33,13 @@
|
|
|
33
33
|
"schema": "bun scripts/build-schema.ts",
|
|
34
34
|
"build:tool-schemas": "bun run scripts/build-tool-schemas.ts",
|
|
35
35
|
"prepublishOnly": "bun run build",
|
|
36
|
-
"test:unit": "bun test src/__tests__ --path-ignore-patterns 'src/__tests__/e2e/**'",
|
|
36
|
+
"test:unit": "bun test src/__tests__ ./test/ ./__tests__/ --path-ignore-patterns 'src/__tests__/e2e/**'",
|
|
37
37
|
"build:tui": "bun scripts/build-tui.ts",
|
|
38
38
|
"smoke:tui": "bun test ./scripts/smoke-tui-pack-install.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@clack/prompts": "^1.6.0",
|
|
42
|
-
"@cortexkit/aft-bridge": "0.
|
|
42
|
+
"@cortexkit/aft-bridge": "0.60.0",
|
|
43
43
|
"@opencode/client": "2.0.11",
|
|
44
44
|
"@opentui/core": "0.4.3",
|
|
45
45
|
"@opentui/solid": "0.4.3",
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"zod": "^4.4.3"
|
|
51
51
|
},
|
|
52
52
|
"optionalDependencies": {
|
|
53
|
-
"@cortexkit/aft-darwin-arm64": "0.
|
|
54
|
-
"@cortexkit/aft-darwin-x64": "0.
|
|
55
|
-
"@cortexkit/aft-linux-arm64": "0.
|
|
56
|
-
"@cortexkit/aft-linux-x64": "0.
|
|
57
|
-
"@cortexkit/aft-win32-arm64": "0.
|
|
58
|
-
"@cortexkit/aft-win32-x64": "0.
|
|
53
|
+
"@cortexkit/aft-darwin-arm64": "0.60.0",
|
|
54
|
+
"@cortexkit/aft-darwin-x64": "0.60.0",
|
|
55
|
+
"@cortexkit/aft-linux-arm64": "0.60.0",
|
|
56
|
+
"@cortexkit/aft-linux-x64": "0.60.0",
|
|
57
|
+
"@cortexkit/aft-win32-arm64": "0.60.0",
|
|
58
|
+
"@cortexkit/aft-win32-x64": "0.60.0"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@opencode-ai/plugin": "^1.17.11",
|
|
@@ -18,8 +18,16 @@ import { debug, log, warn } from "../logger.js";
|
|
|
18
18
|
import { resolvePluginVersion } from "../plugin-version.js";
|
|
19
19
|
import { registerAftConfigErrorRpc, registerAftRpc } from "../rpc/register.js";
|
|
20
20
|
import { hoistedV2ToolConsumers, v2PromptChannelFor } from "../tools/hoisted/v2.js";
|
|
21
|
+
import {
|
|
22
|
+
renameV2ToolReferences,
|
|
23
|
+
V2_HOST_TOOL_NAMES,
|
|
24
|
+
} from "../v2-host-tool-names.js";
|
|
21
25
|
import { registerV2PromptDetachHook } from "../v2-prompt-detach.js";
|
|
26
|
+
import { registerV2ToolHooks } from "../v2-tool-hooks.js";
|
|
27
|
+
import { registerV2SessionLifecycle } from "../v2-session-lifecycle.js";
|
|
28
|
+
import { registerV2WorkflowHints } from "../v2-workflow-hints.js";
|
|
22
29
|
import { createV2RuntimeConsumer } from "../wakes/runtime-consumer.js";
|
|
30
|
+
import { buildHintsForRegisteredTools } from "../workflow-hints.js";
|
|
23
31
|
import {
|
|
24
32
|
buildAftToolDefinitions,
|
|
25
33
|
openCodeHashlineEffective,
|
|
@@ -36,11 +44,15 @@ const defaults = {
|
|
|
36
44
|
registerRpc: registerAftRpc,
|
|
37
45
|
registerConfigErrorRpc: registerAftConfigErrorRpc,
|
|
38
46
|
registerPromptHook: registerV2PromptDetachHook,
|
|
47
|
+
registerToolHooks: registerV2ToolHooks,
|
|
48
|
+
registerWorkflowHints: registerV2WorkflowHints,
|
|
39
49
|
// The prompt server comes from the user-only `opencode` block of the
|
|
40
50
|
// Location's config; without it AFT finds the server it runs inside.
|
|
41
|
-
|
|
51
|
+
// `getConfig` returns the Location's current config, which a live reload
|
|
52
|
+
// replaces; the wake consumer reads it when a completion arrives.
|
|
53
|
+
toolConsumers: (context, config, getConfig = () => config) => ({
|
|
42
54
|
...hoistedV2ToolConsumers(context, v2PromptChannelFor(config?.opencode)),
|
|
43
|
-
...createV2RuntimeConsumer(context),
|
|
55
|
+
...createV2RuntimeConsumer(context, getConfig),
|
|
44
56
|
}),
|
|
45
57
|
acquireBridge,
|
|
46
58
|
releaseBridge,
|
|
@@ -79,7 +91,8 @@ async function bootLocation(context, location, dependencies) {
|
|
|
79
91
|
dependencies,
|
|
80
92
|
);
|
|
81
93
|
const canonicalDirectory = location.project?.canonical ?? directory;
|
|
82
|
-
|
|
94
|
+
let liveConfig = config;
|
|
95
|
+
const consumers = dependencies.toolConsumers(context, config, () => liveConfig);
|
|
83
96
|
// Only a rejected configuration keeps AFT out of a project; there is no
|
|
84
97
|
// config switch that turns it off.
|
|
85
98
|
const isProjectEnabled = createProjectAcceptance(directory, dependencies);
|
|
@@ -107,6 +120,12 @@ async function bootLocation(context, location, dependencies) {
|
|
|
107
120
|
client: context,
|
|
108
121
|
config,
|
|
109
122
|
hashlineEffective: openCodeHashlineEffective(config),
|
|
123
|
+
// The engine's reply text names AFT's command and patch tools ("Prefer
|
|
124
|
+
// `read` tool over bash."), and on OpenCode 2 they are `shell` and
|
|
125
|
+
// `patch`. Every tool request sends this map, beside its session; it is
|
|
126
|
+
// never configure state, because one AFT process can serve an OpenCode 1
|
|
127
|
+
// head, an OpenCode 2 session and a Pi session on the same project.
|
|
128
|
+
hostToolNames: V2_HOST_TOOL_NAMES,
|
|
110
129
|
storageDir: environment.storageDir,
|
|
111
130
|
isProjectEnabled,
|
|
112
131
|
};
|
|
@@ -119,6 +138,9 @@ async function bootLocation(context, location, dependencies) {
|
|
|
119
138
|
const { hashlineEditRegistered } = applyToolSurfaceOverrides(pool, config, registeredTools);
|
|
120
139
|
reportHashlineDowngrade(config, registeredTools, notify);
|
|
121
140
|
toolContext.hashlineEffective = hashlineEditRegistered;
|
|
141
|
+
// The hints are written for OpenCode 1's tool names; name OpenCode 2's.
|
|
142
|
+
const hints = buildHintsForRegisteredTools(config, registeredTools, hashlineEditRegistered);
|
|
143
|
+
const hintsBlock = hints === null ? null : renameV2ToolReferences(hints);
|
|
122
144
|
// Keep this Location's live config keys current when a config file changes.
|
|
123
145
|
// The finalizer that releases the bridge also stops the watch.
|
|
124
146
|
const liveConfigReload = dependencies.startLiveConfigReload({
|
|
@@ -128,10 +150,11 @@ async function bootLocation(context, location, dependencies) {
|
|
|
128
150
|
getConfig: () => toolContext.config,
|
|
129
151
|
setConfig: (next) => {
|
|
130
152
|
toolContext.config = next;
|
|
153
|
+
liveConfig = next;
|
|
131
154
|
},
|
|
132
155
|
notify,
|
|
133
156
|
});
|
|
134
|
-
return { consumers, pool, tools, liveConfigReload, toolContext, directory };
|
|
157
|
+
return { consumers, pool, tools, liveConfigReload, toolContext, directory, hintsBlock };
|
|
135
158
|
}
|
|
136
159
|
|
|
137
160
|
/**
|
|
@@ -210,6 +233,7 @@ export function makeServerEffect(overrides = {}) {
|
|
|
210
233
|
);
|
|
211
234
|
const rpc = yield* dependencies.registerRpc(context, location, runtime.pool);
|
|
212
235
|
yield* Effect.addFinalizer(() => Effect.promise(() => rpc.dispose()));
|
|
236
|
+
yield* registerV2SessionLifecycle(context, runtime.toolContext);
|
|
213
237
|
// OpenCode 2 has no chat.message hook; its session prompt hook is where a
|
|
214
238
|
// new message detaches a waiting bash, as chat.message does on OpenCode 1.
|
|
215
239
|
// The config is read through the tool context so live reloads apply.
|
|
@@ -221,6 +245,7 @@ export function makeServerEffect(overrides = {}) {
|
|
|
221
245
|
pool: runtime.pool,
|
|
222
246
|
projectRoot: runtime.directory,
|
|
223
247
|
getConfig: () => runtime.toolContext.config,
|
|
248
|
+
registerSession: runtime.consumers.registerSession,
|
|
224
249
|
});
|
|
225
250
|
yield* dependencies.registerTools(
|
|
226
251
|
context,
|
|
@@ -228,6 +253,13 @@ export function makeServerEffect(overrides = {}) {
|
|
|
228
253
|
runtime.tools,
|
|
229
254
|
runtime.consumers,
|
|
230
255
|
);
|
|
256
|
+
yield* dependencies.registerToolHooks(
|
|
257
|
+
context,
|
|
258
|
+
runtime.toolContext,
|
|
259
|
+
new Set(Object.keys(runtime.tools)),
|
|
260
|
+
runtime.tools.bash?.description,
|
|
261
|
+
);
|
|
262
|
+
yield* dependencies.registerWorkflowHints(context, runtime.hintsBlock);
|
|
231
263
|
});
|
|
232
264
|
};
|
|
233
265
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
|
|
1
3
|
import { resolvePromptContext } from "./last-assistant-model.js";
|
|
4
|
+
import { isV2PluginContext } from "./v2-context.js";
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* Append an `ignored: true` synthetic user message to a session.
|
|
@@ -24,6 +27,21 @@ export async function sendIgnoredMessage(
|
|
|
24
27
|
sessionID: string,
|
|
25
28
|
text: string,
|
|
26
29
|
): Promise<void> {
|
|
30
|
+
if (isV2PluginContext(client)) {
|
|
31
|
+
const context = client as {
|
|
32
|
+
session: {
|
|
33
|
+
synthetic(input: {
|
|
34
|
+
sessionID: string;
|
|
35
|
+
text: string;
|
|
36
|
+
resume: false;
|
|
37
|
+
}): Effect.Effect<unknown, unknown>;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
// V2 owns the current agent/model itself. Synthetic inbox entries neither
|
|
41
|
+
// switch that context nor resume a turn, and must actually run as Effects.
|
|
42
|
+
await Effect.runPromise(context.session.synthetic({ sessionID, text, resume: false }));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
27
45
|
const typedClient = client as {
|
|
28
46
|
session?: {
|
|
29
47
|
prompt?: (input: unknown) => unknown;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
|
|
1
3
|
import { resolvePromptContext } from "./last-assistant-model.js";
|
|
4
|
+
import { isV2PluginContext } from "./v2-context.js";
|
|
2
5
|
|
|
3
6
|
export const VISION_HOST_TIMEOUT_MS = 1500;
|
|
4
7
|
type Model = { providerID: string; modelID: string };
|
|
@@ -69,12 +72,19 @@ export async function currentSessionVisionCapability(
|
|
|
69
72
|
let capability = state.capabilities.get(key);
|
|
70
73
|
if (!capability) {
|
|
71
74
|
capability = (async () => {
|
|
72
|
-
const api = (client as { provider?: { list?: () =>
|
|
75
|
+
const api = (client as { provider?: { list?: () => unknown } }).provider;
|
|
73
76
|
if (!api?.list) return undefined;
|
|
74
|
-
const list =
|
|
77
|
+
const list = async () => {
|
|
78
|
+
const result = api.list?.();
|
|
79
|
+
return isV2PluginContext(client)
|
|
80
|
+
? Effect.runPromise(result as Effect.Effect<unknown, unknown>)
|
|
81
|
+
: await result;
|
|
82
|
+
};
|
|
75
83
|
const listed = record(await bounded(list));
|
|
76
84
|
const catalog = record(listed?.data) ?? listed;
|
|
77
|
-
const providers =
|
|
85
|
+
const providers = Array.isArray(listed?.data)
|
|
86
|
+
? listed.data
|
|
87
|
+
: (catalog?.all ?? catalog?.providers);
|
|
78
88
|
if (!Array.isArray(providers)) return undefined;
|
|
79
89
|
const provider = providers.map(record).find((entry) => entry?.id === currentModel.providerID);
|
|
80
90
|
const models = provider?.models;
|
|
@@ -83,7 +93,7 @@ export async function currentSessionVisionCapability(
|
|
|
83
93
|
? models.find((entry) => record(entry)?.id === currentModel.modelID)
|
|
84
94
|
: record(models)?.[currentModel.modelID],
|
|
85
95
|
);
|
|
86
|
-
const inputs = record(entry?.modalities)?.input;
|
|
96
|
+
const inputs = record(entry?.capabilities)?.input ?? record(entry?.modalities)?.input;
|
|
87
97
|
return Array.isArray(inputs)
|
|
88
98
|
? inputs.includes("image")
|
|
89
99
|
: typeof entry?.attachment === "boolean"
|
package/src/shared/rpc-client.ts
CHANGED
|
@@ -133,19 +133,46 @@ export class AftRpcClient {
|
|
|
133
133
|
const { signal } = options;
|
|
134
134
|
throwIfAborted(signal);
|
|
135
135
|
|
|
136
|
+
// Fast path: reuse the endpoint that served the last warm response while
|
|
137
|
+
// it is still the newest live port file, skipping discovery's sequential
|
|
138
|
+
// health checks. Anything other than an accepted warm response falls back
|
|
139
|
+
// to the full scan below.
|
|
140
|
+
let placeholder: T | null = null;
|
|
141
|
+
let lastError: unknown = null;
|
|
142
|
+
let answeredPort: number | null = null;
|
|
143
|
+
const cached = this.cachedEndpoint();
|
|
144
|
+
if (cached) {
|
|
145
|
+
try {
|
|
146
|
+
const result = await this.callOne<T>(method, params, cached, signal);
|
|
147
|
+
if (this.looksLikePlaceholder(result)) {
|
|
148
|
+
placeholder = result;
|
|
149
|
+
answeredPort = cached.port;
|
|
150
|
+
} else if (!options.accept || options.accept(result)) {
|
|
151
|
+
return result;
|
|
152
|
+
} else {
|
|
153
|
+
answeredPort = cached.port;
|
|
154
|
+
}
|
|
155
|
+
} catch {
|
|
156
|
+
throwIfAborted(signal);
|
|
157
|
+
}
|
|
158
|
+
this.port = null;
|
|
159
|
+
this.token = null;
|
|
160
|
+
}
|
|
161
|
+
|
|
136
162
|
// Try ALL discovered ports for this project (OpenCode TUI under --port 0
|
|
137
163
|
// loads our plugin twice in the same process, so two RPC servers listen
|
|
138
|
-
// and we have to try both — only one's bridge is actually warm).
|
|
139
|
-
|
|
140
|
-
|
|
164
|
+
// and we have to try both — only one's bridge is actually warm). A port
|
|
165
|
+
// that already answered on the fast path is not asked again.
|
|
166
|
+
const infos = (await this.resolvePortInfos(signal)).filter(
|
|
167
|
+
(info) => info.port !== answeredPort,
|
|
168
|
+
);
|
|
169
|
+
if (infos.length === 0 && placeholder === null) {
|
|
141
170
|
throw new Error("AFT RPC server not available");
|
|
142
171
|
}
|
|
143
172
|
|
|
144
173
|
// First pass: try every port. Prefer responses that look like "warm
|
|
145
174
|
// bridge" output (i.e. not the synthetic `status: "not_initialized"`
|
|
146
175
|
// placeholder served when this instance's bridge hasn't been spawned).
|
|
147
|
-
let placeholder: T | null = null;
|
|
148
|
-
let lastError: unknown = null;
|
|
149
176
|
for (const info of infos) {
|
|
150
177
|
throwIfAborted(signal);
|
|
151
178
|
try {
|
|
@@ -250,6 +277,20 @@ export class AftRpcClient {
|
|
|
250
277
|
return status === "not_initialized";
|
|
251
278
|
}
|
|
252
279
|
|
|
280
|
+
/**
|
|
281
|
+
* The endpoint that served the last warm response, if it is still the newest
|
|
282
|
+
* live port file for this project. Discovery tries ports newest first and
|
|
283
|
+
* returns the first warm answer, so while this holds the full scan would
|
|
284
|
+
* reach the same server first. Reading the port files also drops entries
|
|
285
|
+
* whose owning process has died, so a dead server is never reused.
|
|
286
|
+
*/
|
|
287
|
+
private cachedEndpoint(): PortInfo | null {
|
|
288
|
+
if (this.port === null) return null;
|
|
289
|
+
const [newest] = this.readAllPortFiles();
|
|
290
|
+
if (!newest || newest.port !== this.port || newest.token !== this.token) return null;
|
|
291
|
+
return newest;
|
|
292
|
+
}
|
|
293
|
+
|
|
253
294
|
/** Check if any RPC server is reachable. */
|
|
254
295
|
async isAvailable(): Promise<boolean> {
|
|
255
296
|
try {
|
package/src/shared/rpc-server.ts
CHANGED
|
@@ -319,6 +319,10 @@ export class AftRpcServer {
|
|
|
319
319
|
return json({ error: "Invalid JSON" }, 400);
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
+
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
|
323
|
+
return json({ error: "Expected a JSON object" }, 400);
|
|
324
|
+
}
|
|
325
|
+
|
|
322
326
|
if (!tokensMatch(typeof params.token === "string" ? params.token : "", this.token)) {
|
|
323
327
|
return json({ error: "Forbidden" }, 403);
|
|
324
328
|
}
|
|
@@ -453,6 +457,12 @@ export class AftRpcServer {
|
|
|
453
457
|
return;
|
|
454
458
|
}
|
|
455
459
|
|
|
460
|
+
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
|
461
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
462
|
+
res.end(JSON.stringify({ error: "Expected a JSON object" }));
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
456
466
|
if (!tokensMatch(typeof params.token === "string" ? params.token : "", this.token)) {
|
|
457
467
|
res.writeHead(403, { "Content-Type": "application/json" });
|
|
458
468
|
res.end(JSON.stringify({ error: "Forbidden" }));
|