Skip to content

Commit cfc56f5

Browse files
authored
Merge pull request #73 from bglusman/release/docs-and-version-prep
2 parents 048920f + 7da2735 commit cfc56f5

76 files changed

Lines changed: 10276 additions & 202 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/secret-scan.yml

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
with:
2020
persist-credentials: false
2121
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
22-
fetch-depth: 0
22+
fetch-depth: 1
2323
- name: Install gitleaks
2424
env:
2525
GITLEAKS_VERSION: 8.24.3
@@ -32,31 +32,32 @@ jobs:
3232
- name: Run gitleaks on PR range
3333
if: github.event_name == 'pull_request'
3434
env:
35-
BASE_REF: ${{ github.event.pull_request.base.ref }}
35+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
3636
GITHUB_TOKEN: ${{ github.token }}
3737
run: |
3838
set -euo pipefail
39-
case "$BASE_REF" in
40-
""|/*|*..*|*"@{"*|*\\*|*[!A-Za-z0-9._/-]*)
41-
echo "Refusing unsafe base ref: $BASE_REF" >&2
42-
exit 1
43-
;;
44-
esac
39+
if ! printf '%s' "$BASE_SHA" | grep -Eq '^[0-9a-f]{40}$'; then
40+
echo "Refusing unsafe base SHA: $BASE_SHA" >&2
41+
exit 1
42+
fi
4543
auth_header="$(printf 'x-access-token:%s' "${GITHUB_TOKEN}" | base64 | tr -d '\n')"
4644
echo "::add-mask::${auth_header}"
47-
git -c "http.https://github.com/.extraheader=AUTHORIZATION: basic ${auth_header}" fetch --no-tags origin "+refs/heads/${BASE_REF}:refs/remotes/origin/${BASE_REF}"
48-
base="$(git merge-base HEAD "origin/${BASE_REF}")"
49-
gitleaks detect --config .gitleaks.toml --redact -v --exit-code=2 --log-opts="${base}..HEAD"
45+
git -c "http.https://github.com/.extraheader=AUTHORIZATION: basic ${auth_header}" fetch --no-tags --depth=1 origin "${BASE_SHA}"
46+
gitleaks detect --config .gitleaks.toml --redact -v --exit-code=2 --log-opts="${BASE_SHA}..HEAD"
5047
- name: Run gitleaks on pushed commits
5148
if: github.event_name == 'push'
5249
env:
5350
BEFORE_SHA: ${{ github.event.before }}
5451
AFTER_SHA: ${{ github.sha }}
52+
GITHUB_TOKEN: ${{ github.token }}
5553
run: |
5654
set -euo pipefail
5755
if [ -z "$BEFORE_SHA" ] || [ "$BEFORE_SHA" = "0000000000000000000000000000000000000000" ]; then
5856
range="HEAD~1..HEAD"
5957
else
58+
auth_header="$(printf 'x-access-token:%s' "${GITHUB_TOKEN}" | base64 | tr -d '\n')"
59+
echo "::add-mask::${auth_header}"
60+
git -c "http.https://github.com/.extraheader=AUTHORIZATION: basic ${auth_header}" fetch --no-tags --depth=1 origin "${BEFORE_SHA}"
6061
range="${BEFORE_SHA}..${AFTER_SHA}"
6162
fi
6263
gitleaks detect --config .gitleaks.toml --redact -v --exit-code=2 --log-opts="$range"

.github/workflows/wardwright-release.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
id-token: write
4040
steps:
4141
- name: Checkout
42-
uses: actions/checkout@v4
42+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
4343
with:
4444
persist-credentials: false
4545

@@ -80,7 +80,7 @@ jobs:
8080
scripts/smoke-burrito-binary.sh app/burrito_out/wardwright_${{ matrix.id }}
8181
8282
- name: Upload artifact
83-
uses: actions/upload-artifact@v4
83+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
8484
with:
8585
name: binary-${{ matrix.id }}
8686
path: app/_tinfoil/*
@@ -100,7 +100,7 @@ jobs:
100100
contents: write
101101
steps:
102102
- name: Checkout
103-
uses: actions/checkout@v4
103+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
104104
with:
105105
persist-credentials: false
106106

@@ -112,7 +112,7 @@ jobs:
112112
gleam-version: "1.16.0"
113113

114114
- name: Download all artifacts
115-
uses: actions/download-artifact@v4
115+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
116116
with:
117117
path: artifacts
118118
merge-multiple: true
@@ -136,7 +136,7 @@ jobs:
136136
contents: read
137137
steps:
138138
- name: Checkout
139-
uses: actions/checkout@v4
139+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
140140
with:
141141
persist-credentials: false
142142

@@ -152,7 +152,7 @@ jobs:
152152
run: mix deps.get
153153

154154
- name: Download all artifacts
155-
uses: actions/download-artifact@v4
155+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
156156
with:
157157
path: artifacts
158158
merge-multiple: true

README.md

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,9 @@ individual models can be configured for API-key or open access.
2626

2727
## Install
2828

29-
Wardwright publishes early native binaries for macOS and Linux. The latest
30-
stable published release is `v0.0.10`. The current source tree is preparing
31-
`v0.1.0-rc.1`, which adds the Lustre workbench migration, framework-adapter
32-
recipes, and local agent-adapter install/probe support while keeping stronger
29+
Wardwright publishes early native binaries for macOS and Linux. The `v0.0.11`
30+
release adds the Lustre workbench migration, framework-adapter recipes, and
31+
local agent-adapter install/probe support while keeping stronger
3332
state-fidelity claims explicitly limited.
3433

3534
### macOS Homebrew
@@ -60,13 +59,7 @@ WARDWRIGHT_BIND=127.0.0.1:8787 \
6059
For a pinned release:
6160

6261
```bash
63-
curl -fsSL https://raw.githubusercontent.com/bglusman/wardwright/main/scripts/install.sh | sh -s -- --version v0.0.10
64-
```
65-
66-
For the release candidate after it is tagged, pin it explicitly:
67-
68-
```bash
69-
curl -fsSL https://raw.githubusercontent.com/bglusman/wardwright/main/scripts/install.sh | sh -s -- --version v0.1.0-rc.1
62+
curl -fsSL https://raw.githubusercontent.com/bglusman/wardwright/main/scripts/install.sh | sh -s -- --version v0.0.11
7063
```
7164

7265
Set `WARDWRIGHT_ADMIN_TOKEN` before exposing Wardwright beyond loopback. See
@@ -131,8 +124,8 @@ Wardwright exposes:
131124

132125
See [Agent Authoring](docs/agent-authoring.md) for the review loop external
133126
agents should follow before activating a model.
134-
Release-candidate adapter install, doctor, pair, probe, privacy, cleanup, and
135-
fallback behavior is documented in [Agent Adapters](docs/agent-adapters.md).
127+
Adapter install, doctor, pair, probe, privacy, cleanup, and fallback behavior
128+
is documented in [Agent Adapters](docs/agent-adapters.md).
136129

137130
`wardwright admin` opens the workbench in your browser. If nothing is listening
138131
on the configured `WARDWRIGHT_BIND` port, it starts a local background service

app/gleam.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name = "wardwright"
2-
version = "0.1.0"
2+
version = "0.0.11"
33
target = "erlang"
44

55
[dependencies]

app/lib/wardwright.ex

Lines changed: 154 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ defmodule Wardwright do
1818
@choice_index_key "index"
1919
@choices_key "choices"
2020
@content_key "content"
21+
@enabled_key "enabled"
2122
@finish_reason_key "finish_reason"
2223
@message_key "message"
2324
@messages_key "messages"
@@ -30,6 +31,22 @@ defmodule Wardwright do
3031
@recording_manual_mode "manual"
3132
@refusal_key "refusal"
3233
@role_key "role"
34+
@server_tool_beam_module_engine "beam_module"
35+
@server_tool_builtin_engine "builtin"
36+
@server_tool_dune_engine "dune"
37+
@server_tool_elixir_module_engine "elixir_module"
38+
@server_tool_engine_key "engine"
39+
@server_tool_input_key "input"
40+
@server_tool_limits_key "limits"
41+
@server_tool_module_key "module"
42+
@server_tool_parameters_key "parameters"
43+
@server_tool_path_key "path"
44+
@server_tools_key "server_tools"
45+
@server_tool_policy_cache_status "wardwright_policy_cache_status"
46+
@server_tool_snippet_id_key "snippet_id"
47+
@server_tool_source_key "source"
48+
@tool_mediation_key "tool_mediation"
49+
@tool_mediation_rules_key "rules"
3350
@delta_key "delta"
3451
@preserved_delta_fields_key "preserved_delta_fields"
3552
@system_fingerprint_key "system_fingerprint"
@@ -88,7 +105,8 @@ defmodule Wardwright do
88105
@recording_adapted_agents_key => @recording_auto_mode,
89106
@recording_default_key => @recording_manual_mode,
90107
@recording_generic_clients_key => @recording_manual_mode
91-
}
108+
},
109+
@tool_mediation_key => %{@tool_mediation_rules_key => []}
92110
}
93111
end
94112

@@ -932,7 +950,9 @@ defmodule Wardwright do
932950
version -> version
933951
end),
934952
@description_key => normalize_description(config),
935-
@recording_key => normalize_recording(Map.get(config, @recording_key, %{}))
953+
@recording_key => normalize_recording(Map.get(config, @recording_key, %{})),
954+
@server_tools_key => normalize_server_tools(Map.get(config, @server_tools_key, [])),
955+
@tool_mediation_key => normalize_tool_mediation(Map.get(config, @tool_mediation_key, %{}))
936956
}
937957
end
938958

@@ -1064,6 +1084,138 @@ defmodule Wardwright do
10641084

10651085
defp normalize_vcr(_), do: %{"mode" => "metadata_only"}
10661086

1087+
defp normalize_tool_mediation(%{@tool_mediation_rules_key => rules} = mediation) when is_list(rules) do
1088+
mediation
1089+
|> Map.take(["mode", @tool_mediation_rules_key])
1090+
|> Map.put(@tool_mediation_rules_key, Enum.filter(rules, &is_map/1))
1091+
end
1092+
1093+
defp normalize_tool_mediation(rules) when is_list(rules),
1094+
do: %{@tool_mediation_rules_key => Enum.filter(rules, &is_map/1)}
1095+
1096+
defp normalize_tool_mediation(_mediation), do: %{@tool_mediation_rules_key => []}
1097+
1098+
defp normalize_server_tools(tools) when is_list(tools) do
1099+
tools
1100+
|> Enum.flat_map(fn
1101+
%{@enabled_key => false} = tool ->
1102+
tool
1103+
|> Map.delete(@enabled_key)
1104+
|> normalize_server_tool()
1105+
|> Enum.map(&Map.put(&1, @enabled_key, false))
1106+
1107+
%{} = tool ->
1108+
normalize_server_tool(tool)
1109+
1110+
@server_tool_policy_cache_status ->
1111+
[%{@name_key => @server_tool_policy_cache_status, @server_tool_engine_key => @server_tool_builtin_engine}]
1112+
1113+
_tool ->
1114+
[]
1115+
end)
1116+
end
1117+
1118+
defp normalize_server_tools(_tools), do: []
1119+
1120+
defp normalize_server_tool(tool) do
1121+
engine = normalize_server_tool_engine(Map.get(tool, @server_tool_engine_key), tool)
1122+
1123+
cond do
1124+
engine == @server_tool_builtin_engine ->
1125+
tool
1126+
|> Map.get(@name_key, "")
1127+
|> to_string()
1128+
|> String.trim()
1129+
|> case do
1130+
@server_tool_policy_cache_status ->
1131+
[%{@name_key => @server_tool_policy_cache_status, @server_tool_engine_key => @server_tool_builtin_engine}]
1132+
1133+
_other ->
1134+
[]
1135+
end
1136+
1137+
engine == @server_tool_dune_engine ->
1138+
tool
1139+
|> normalize_dune_server_tool()
1140+
|> List.wrap()
1141+
1142+
engine == @server_tool_beam_module_engine ->
1143+
tool
1144+
|> normalize_beam_module_server_tool()
1145+
|> List.wrap()
1146+
1147+
true ->
1148+
[]
1149+
end
1150+
end
1151+
1152+
defp normalize_server_tool_engine(@server_tool_elixir_module_engine, _tool), do: @server_tool_beam_module_engine
1153+
1154+
defp normalize_server_tool_engine(engine, _tool)
1155+
when engine in [@server_tool_builtin_engine, @server_tool_dune_engine, @server_tool_beam_module_engine],
1156+
do: engine
1157+
1158+
defp normalize_server_tool_engine(_engine, tool) do
1159+
cond do
1160+
present_string?(Map.get(tool, @server_tool_source_key)) or
1161+
present_string?(Map.get(tool, @server_tool_snippet_id_key)) ->
1162+
@server_tool_dune_engine
1163+
1164+
present_string?(Map.get(tool, @server_tool_module_key)) or present_string?(Map.get(tool, @server_tool_path_key)) ->
1165+
@server_tool_beam_module_engine
1166+
1167+
true ->
1168+
@server_tool_builtin_engine
1169+
end
1170+
end
1171+
1172+
defp normalize_dune_server_tool(tool) do
1173+
name = tool |> Map.get(@name_key, "") |> to_string() |> String.trim()
1174+
1175+
if name != "" and
1176+
(present_string?(Map.get(tool, @server_tool_source_key)) or
1177+
present_string?(Map.get(tool, @server_tool_snippet_id_key))) do
1178+
tool
1179+
|> Map.take([
1180+
@description_key,
1181+
@name_key,
1182+
@server_tool_input_key,
1183+
@server_tool_limits_key,
1184+
@server_tool_parameters_key,
1185+
@server_tool_snippet_id_key,
1186+
@server_tool_source_key
1187+
])
1188+
|> Map.put(@server_tool_engine_key, @server_tool_dune_engine)
1189+
|> Map.put(@name_key, name)
1190+
end
1191+
end
1192+
1193+
defp normalize_beam_module_server_tool(tool) do
1194+
module = tool |> Map.get(@server_tool_module_key, "") |> to_string() |> String.trim()
1195+
path = tool |> Map.get(@server_tool_path_key, "") |> to_string() |> String.trim()
1196+
1197+
if module != "" or path != "" do
1198+
tool
1199+
|> Map.take([
1200+
@description_key,
1201+
@name_key,
1202+
@server_tool_module_key,
1203+
@server_tool_parameters_key,
1204+
@server_tool_path_key
1205+
])
1206+
|> Map.put(@server_tool_engine_key, @server_tool_beam_module_engine)
1207+
|> put_present(@name_key, tool |> Map.get(@name_key, "") |> to_string() |> String.trim())
1208+
|> put_present(@server_tool_module_key, module)
1209+
|> put_present(@server_tool_path_key, path)
1210+
end
1211+
end
1212+
1213+
defp present_string?(value) when is_binary(value), do: String.trim(value) != ""
1214+
defp present_string?(_value), do: false
1215+
1216+
defp put_present(map, _key, ""), do: map
1217+
defp put_present(map, key, value), do: Map.put(map, key, value)
1218+
10671219
defp validate_config(%{"model_id" => model_id, "targets" => targets} = config) do
10681220
cond do
10691221
model_id == "" ->

app/lib/wardwright/agent_adapters/claude_code_pack.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ defmodule Wardwright.AgentAdapters.ClaudeCodePack do
44
alias Wardwright.AgentAdapters.CanonicalJson
55

66
@adapter_id "wardwright-claude-code"
7-
@adapter_version "0.1.0-rc.1"
7+
@adapter_version "0.0.11"
88
@config_path ".wardwright/adapters/claude-code-adapter.json"
99
@default_gateway_url "http://127.0.0.1:8787"
1010
@fidelity "prompt_handoff"

app/lib/wardwright/agent_adapters/omp_pack.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ defmodule Wardwright.AgentAdapters.OmpPack do
44
alias Wardwright.AgentAdapters.CanonicalJson
55

66
@adapter_id "wardwright-omp"
7-
@adapter_version "0.1.0-rc.1"
7+
@adapter_version "0.0.11"
88
@config_path ".omp/wardwright-adapter.json"
99
@default_gateway_url "http://127.0.0.1:8787"
1010
@key_gateway_url "gateway_url"

app/lib/wardwright/agent_adapters/pi_pack.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ defmodule Wardwright.AgentAdapters.PiPack do
44
alias Wardwright.AgentAdapters.CanonicalJson
55

66
@adapter_id "wardwright-pi"
7-
@adapter_version "0.1.0-rc.1"
7+
@adapter_version "0.0.11"
88
@config_path ".wardwright/adapters/pi-adapter.json"
99
@default_gateway_url "http://127.0.0.1:8787"
1010
@key_export_only "export_only"

app/lib/wardwright/router.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1112,7 +1112,7 @@ defmodule Wardwright.Router do
11121112
request
11131113
|> Map.put("wardwright_attempt_index", attempt_index)
11141114
|> Wardwright.StructuredOutputRetryFeedback.add(attempt_index, structured_config)
1115-
|> then(&Wardwright.complete_selected_model(decision.selected_model, &1, config))
1115+
|> then(&Wardwright.ServerTools.complete_selected_model(decision.selected_model, &1, config))
11161116
|> Map.put_new(:structured_output, nil)
11171117
end)
11181118
end

0 commit comments

Comments
 (0)