@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.
Files changed (93) hide show
  1. package/dist/bash-wait-detach.d.ts.map +1 -1
  2. package/dist/bg-notifications.d.ts +13 -0
  3. package/dist/bg-notifications.d.ts.map +1 -1
  4. package/dist/bridge-bootstrap.d.ts +6 -6
  5. package/dist/bridge-bootstrap.d.ts.map +1 -1
  6. package/dist/config-live-reload.d.ts +36 -8
  7. package/dist/config-live-reload.d.ts.map +1 -1
  8. package/dist/config.d.ts +72 -17
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/configure-warnings.d.ts.map +1 -1
  11. package/dist/entry/server.js +5079 -2255
  12. package/dist/hooks/auto-update-checker/checker.d.ts.map +1 -1
  13. package/dist/hooks/auto-update-checker/index.d.ts.map +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +4674 -1999
  16. package/dist/lsp-auto-install.d.ts.map +1 -1
  17. package/dist/lsp-github-install.d.ts +11 -2
  18. package/dist/lsp-github-install.d.ts.map +1 -1
  19. package/dist/notifications.d.ts +1 -1
  20. package/dist/notifications.d.ts.map +1 -1
  21. package/dist/rpc/contract.d.ts +1 -1
  22. package/dist/rpc/contract.d.ts.map +1 -1
  23. package/dist/rpc/register.d.ts +3 -1
  24. package/dist/rpc/register.d.ts.map +1 -1
  25. package/dist/shared/ignored-message.d.ts.map +1 -1
  26. package/dist/shared/read-vision.d.ts.map +1 -1
  27. package/dist/shared/rpc-client.d.ts +8 -0
  28. package/dist/shared/rpc-client.d.ts.map +1 -1
  29. package/dist/shared/rpc-server.d.ts.map +1 -1
  30. package/dist/shared/session-directory.d.ts +7 -5
  31. package/dist/shared/session-directory.d.ts.map +1 -1
  32. package/dist/shared/status.d.ts +10 -4
  33. package/dist/shared/status.d.ts.map +1 -1
  34. package/dist/shared/v2-context.d.ts +3 -0
  35. package/dist/shared/v2-context.d.ts.map +1 -0
  36. package/dist/subc-tool-schemas.d.ts +23 -1
  37. package/dist/subc-tool-schemas.d.ts.map +1 -1
  38. package/dist/tool-registration.d.ts +6 -3
  39. package/dist/tool-registration.d.ts.map +1 -1
  40. package/dist/tools/_shared.d.ts +2 -5
  41. package/dist/tools/_shared.d.ts.map +1 -1
  42. package/dist/tools/bash.d.ts +17 -0
  43. package/dist/tools/bash.d.ts.map +1 -1
  44. package/dist/tools/bash_watch.d.ts +11 -1
  45. package/dist/tools/bash_watch.d.ts.map +1 -1
  46. package/dist/tools/definitions/v2.d.ts +11 -1
  47. package/dist/tools/definitions/v2.d.ts.map +1 -1
  48. package/dist/tools/hoisted.d.ts +18 -0
  49. package/dist/tools/hoisted.d.ts.map +1 -1
  50. package/dist/tools/inspect.d.ts +10 -1
  51. package/dist/tools/inspect.d.ts.map +1 -1
  52. package/dist/tools/permissions.d.ts +2 -1
  53. package/dist/tools/permissions.d.ts.map +1 -1
  54. package/dist/tools/search.d.ts.map +1 -1
  55. package/dist/tools/semantic.d.ts.map +1 -1
  56. package/dist/tui/aft-rpc.d.ts +14 -0
  57. package/dist/tui/aft-rpc.d.ts.map +1 -1
  58. package/dist/tui/v2-status.d.ts.map +1 -1
  59. package/dist/types.d.ts +7 -1
  60. package/dist/types.d.ts.map +1 -1
  61. package/dist/v2-host-tool-names.d.ts +43 -0
  62. package/dist/v2-host-tool-names.d.ts.map +1 -0
  63. package/dist/v2-prompt-detach.d.ts +1 -0
  64. package/dist/v2-prompt-detach.d.ts.map +1 -1
  65. package/dist/v2-session-lifecycle.d.ts +22 -0
  66. package/dist/v2-session-lifecycle.d.ts.map +1 -0
  67. package/dist/v2-tool-hooks.d.ts +18 -0
  68. package/dist/v2-tool-hooks.d.ts.map +1 -0
  69. package/dist/v2-workflow-hints.d.ts +4 -0
  70. package/dist/v2-workflow-hints.d.ts.map +1 -0
  71. package/dist/wakes/runtime-consumer.d.ts +6 -1
  72. package/dist/wakes/runtime-consumer.d.ts.map +1 -1
  73. package/dist/wakes/session-delivery.d.ts +12 -7
  74. package/dist/wakes/session-delivery.d.ts.map +1 -1
  75. package/package.json +9 -9
  76. package/src/entry/server-runtime.mjs +36 -4
  77. package/src/shared/ignored-message.ts +18 -0
  78. package/src/shared/read-vision.ts +14 -4
  79. package/src/shared/rpc-client.ts +46 -5
  80. package/src/shared/rpc-server.ts +10 -0
  81. package/src/shared/session-directory.ts +67 -19
  82. package/src/shared/status.ts +40 -9
  83. package/src/shared/v2-context.ts +7 -0
  84. package/src/tui/aft-rpc.ts +11 -0
  85. package/src/tui/index.tsx +20 -16
  86. package/src/tui/sidebar-view.tsx +9 -1
  87. package/src/tui/v2-status.ts +8 -5
  88. package/src/tui/v2.tsx +50 -0
  89. package/src/tui-compiled/aft-rpc.ts +11 -0
  90. package/src/tui-compiled/index.tsx +27 -16
  91. package/src/tui-compiled/sidebar-view.tsx +11 -2
  92. package/src/tui-compiled/v2-status.ts +8 -5
  93. 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,EAAE,MAAM,qBAAqB,CAAC;AAE1D,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,CAuJ9E"}
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,CAyIhF"}
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"}
@@ -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;AAEzD,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAajB,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;AAsBhG;;;;;;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"}
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. */
@@ -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;AAC9D,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,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAC;IACnB,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC;CACrD"}
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":"AAaA,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;CACxB;AAuDD;;;;;;;;;;;;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,CAenD;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"}
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;AAM/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,KAAK,kBAAkB,EAAqB,MAAM,uBAAuB,CAAC;AAEnF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;CACtC;AAkJD,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;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,iBAAiB,GAAG,iBAAiB,CAiErF"}
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` value on every prompt AFT admits into a session itself
4
- * (background-completion wakes). OpenCode 2 runs every prompt, AFT's own
5
- * included, through the session `prompt` hook; the hook reads this marker to
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 resume: false;
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 through the host's native inbox. Completion text is
43
- * model-directed, while status records are synthetic and never resume a turn.
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,EAAE,MAAM,QAAQ,CAAC;AAEhC;;;;;GAKG;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,MAAM,EAAE,KAAK,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,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;IA0BlE,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC;IAe7D,OAAO,IAAI,IAAI;CAKhB"}
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.58.2",
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.58.2",
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.58.2",
54
- "@cortexkit/aft-darwin-x64": "0.58.2",
55
- "@cortexkit/aft-linux-arm64": "0.58.2",
56
- "@cortexkit/aft-linux-x64": "0.58.2",
57
- "@cortexkit/aft-win32-arm64": "0.58.2",
58
- "@cortexkit/aft-win32-x64": "0.58.2"
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
- toolConsumers: (context, config) => ({
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
- const consumers = dependencies.toolConsumers(context, config);
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?: () => Promise<unknown> } }).provider;
75
+ const api = (client as { provider?: { list?: () => unknown } }).provider;
73
76
  if (!api?.list) return undefined;
74
- const list = api.list.bind(api);
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 = catalog?.all ?? catalog?.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"
@@ -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
- const infos = await this.resolvePortInfos(signal);
140
- if (infos.length === 0) {
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 {
@@ -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" }));