diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 84caa8a..8be9956 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.7.6" + ".": "0.7.14" } diff --git a/Makefile b/Makefile index 4cd736b..ef137d2 100644 --- a/Makefile +++ b/Makefile @@ -21,10 +21,10 @@ PUBLISH_DRY_RUN_TOKEN ?= wpy_ci_dry_run_0123456789abcdef0123456789abcdef test: test-sqlite test-sqlite: clean test-static - cd $(TEST_DIR) && $(WIPPY) run test --profile sqlite + cd $(TEST_DIR) && $(WIPPY) test --profile sqlite test-postgres: test-static - cd $(TEST_DIR) && $(WIPPY) run test --profile postgres \ + cd $(TEST_DIR) && $(WIPPY) test --profile postgres \ --set "vars.postgres_host=$(DATAFLOW_PG_HOST)" \ --set "vars.postgres_port=$(DATAFLOW_PG_PORT)" \ --set "vars.postgres_database=$(DATAFLOW_PG_DATABASE)" \ diff --git a/src/_index.yaml b/src/_index.yaml index 4e07d95..89809c7 100644 --- a/src/_index.yaml +++ b/src/_index.yaml @@ -1,4 +1,4 @@ -version: "0.7.6" # x-release-please-version +version: "0.7.14" # x-release-please-version namespace: userspace.dataflow entries: diff --git a/src/api/_index.yaml b/src/api/_index.yaml index 064982f..453549e 100644 --- a/src/api/_index.yaml +++ b/src/api/_index.yaml @@ -8,10 +8,6 @@ entries: meta: comment: Cancel a running dataflow description: Dataflow management HTTP endpoints - depends_on: - - app:api - - ns:userspace.dataflow - - ns:userspace.dataflow.persist router: app:api source: file://cancel_dataflow.lua modules: @@ -27,10 +23,6 @@ entries: meta: comment: Cancel dataflow endpoint description: Dataflow management HTTP endpoints - depends_on: - - app:api - - ns:userspace.dataflow - - ns:userspace.dataflow.persist router: app:api method: POST func: cancel_dataflow @@ -42,10 +34,6 @@ entries: meta: comment: Deliver a signal to a waiting dataflow description: Dataflow management HTTP endpoints - depends_on: - - app:api - - ns:userspace.dataflow - - ns:userspace.dataflow.persist router: app:api source: file://signal_dataflow.lua modules: @@ -62,10 +50,6 @@ entries: meta: comment: Signal dataflow endpoint description: Dataflow management HTTP endpoints - depends_on: - - app:api - - ns:userspace.dataflow - - ns:userspace.dataflow.persist router: app:api method: POST func: signal_dataflow @@ -77,10 +61,6 @@ entries: meta: comment: Get complete dataflow details with nodes and data description: Dataflow management HTTP endpoints - depends_on: - - app:api - - ns:userspace.dataflow - - ns:userspace.dataflow.persist router: app:api source: file://get_dataflow.lua modules: @@ -98,10 +78,6 @@ entries: meta: comment: Get dataflow endpoint description: Dataflow management HTTP endpoints - depends_on: - - app:api - - ns:userspace.dataflow - - ns:userspace.dataflow.persist router: app:api method: GET func: get_dataflow @@ -113,10 +89,6 @@ entries: meta: comment: Get dataflow nodes only description: Dataflow management HTTP endpoints - depends_on: - - app:api - - ns:userspace.dataflow - - ns:userspace.dataflow.persist router: app:api source: file://get_dataflow_nodes.lua modules: @@ -133,10 +105,6 @@ entries: meta: comment: Get dataflow nodes endpoint description: Dataflow management HTTP endpoints - depends_on: - - app:api - - ns:userspace.dataflow - - ns:userspace.dataflow.persist router: app:api method: GET func: get_dataflow_nodes @@ -148,10 +116,6 @@ entries: meta: comment: List user's dataflows with filtering and pagination description: Dataflow management HTTP endpoints - depends_on: - - app:api - - ns:userspace.dataflow - - ns:userspace.dataflow.persist router: app:api source: file://list_dataflows.lua modules: @@ -167,10 +131,6 @@ entries: meta: comment: List dataflows endpoint description: Dataflow management HTTP endpoints - depends_on: - - app:api - - ns:userspace.dataflow - - ns:userspace.dataflow.persist router: app:api method: GET func: list_dataflows @@ -182,10 +142,6 @@ entries: meta: comment: Terminate a dataflow forcefully description: Dataflow management HTTP endpoints - depends_on: - - app:api - - ns:userspace.dataflow - - ns:userspace.dataflow.persist router: app:api source: file://terminate_dataflow.lua modules: @@ -194,19 +150,14 @@ entries: imports: client: userspace.dataflow:client method: handler - + # userspace.dataflow.api:terminate_dataflow.endpoint - name: terminate_dataflow.endpoint kind: http.endpoint meta: comment: Terminate dataflow endpoint description: Dataflow management HTTP endpoints - depends_on: - - app:api - - ns:userspace.dataflow - - ns:userspace.dataflow.persist router: app:api method: POST func: terminate_dataflow path: /dataflows/{id}/terminate - \ No newline at end of file diff --git a/src/node/agent/node.lua b/src/node/agent/node.lua index dce24b7..f1e0181 100644 --- a/src/node/agent/node.lua +++ b/src/node/agent/node.lua @@ -1422,6 +1422,33 @@ local function configure_tool_wrappers(caller, agent_instance, n, agent_id, mode end end +local function exit_schema_violation(schema: any, arguments: any): string? + if type(schema) ~= "table" then return nil end + if type(arguments) ~= "table" then + return "the finish call carried " .. type(arguments) + .. " arguments where the exit schema requires an object" + end + + local missing = {} :: { string } + local required = (schema :: any).required + if type(required) == "table" then + for _, key in ipairs(required :: { any }) do + if type(key) == "string" and (arguments :: any)[key] == nil then + table.insert(missing, key :: string) + end + end + end + if #missing > 0 then + return "the finish call is missing required fields: " .. table.concat(missing, ", ") + end + + local properties = (schema :: any).properties + if next(arguments :: any) == nil and type(properties) == "table" and next(properties :: any) ~= nil then + return "the finish call carried no arguments where the exit schema declares properties" + end + return nil +end + local function process_tool_results(n, tool_results, iteration, exit_tool_name, agent_result: any, arena_config, session_context, tool_call_to_node_id) local control_responses = {} @@ -1458,8 +1485,24 @@ local function process_tool_results(n, tool_results, iteration, exit_tool_name, final_result = validated_result end else - task_complete = true - final_result = exit_arguments or { success = false, error = "Exit tool called without arguments" } + local violation = exit_schema_violation(arena_config.exit_schema, exit_arguments) + if violation then + n:data(agent_consts.DATA_TYPE.AGENT_OBSERVATION, violation, { + key = iteration .. "_exit_validation_failed", + content_type = consts.CONTENT_TYPE.TEXT, + node_id = n.node_id, + metadata = { + iteration = iteration, + is_error = true, + tool_call_id = original_tool_call.id, + tool_name = original_tool_call.name, + exit_validation = true + } + }) + else + task_complete = true + final_result = exit_arguments + end end break end diff --git a/src/node/agent/process_tool_results_test.lua b/src/node/agent/process_tool_results_test.lua index af19642..cf7400d 100644 --- a/src/node/agent/process_tool_results_test.lua +++ b/src/node/agent/process_tool_results_test.lua @@ -125,6 +125,70 @@ local function define_tests() test.eq((final_result :: any).answer, "done", "final result carries the finish arguments") test.eq(#recorded, 0, "success path does not record sibling tool observations") end) + + it("rejects a finish call that omits a required exit field", function() + local process_tool_results = agent_node._test.process_tool_results + local n, recorded = make_recording_node() + local agent_result = { + tool_calls = { + { id = "call_finish", name = "finish", arguments = {} }, + } + } + + local _responses, _delegations, task_complete, final_result = process_tool_results( + n, + {}, + 1, + "finish", + agent_result, + { + exit_schema = { + type = "object", + properties = { answer = { type = "string" } }, + required = { "answer" } + } + }, + {}, + {} + ) + + test.eq(task_complete, false, "invalid finish does not complete the task") + test.is_nil(final_result, "invalid finish has no final result") + local rejection = find_by_tool_call_id(recorded, "call_finish") + test.not_nil(rejection, "schema rejection is recorded") + test.contains(rejection.content, "answer", "rejection identifies the missing field") + end) + + it("accepts a finish call that satisfies the exit schema", function() + local process_tool_results = agent_node._test.process_tool_results + local n, recorded = make_recording_node() + local agent_result = { + tool_calls = { + { id = "call_finish", name = "finish", arguments = { answer = "done" } }, + } + } + + local _responses, _delegations, task_complete, final_result = process_tool_results( + n, + {}, + 1, + "finish", + agent_result, + { + exit_schema = { + type = "object", + properties = { answer = { type = "string" } }, + required = { "answer" } + } + }, + {}, + {} + ) + + test.eq(task_complete, true, "valid finish completes the task") + test.eq((final_result :: any).answer, "done", "valid finish preserves its result") + test.eq(#recorded, 0, "valid finish records no rejection") + end) end) end diff --git a/test/.wippy.yaml b/test/.wippy.yaml index 9db49e7..24dcdbd 100644 --- a/test/.wippy.yaml +++ b/test/.wippy.yaml @@ -1,4 +1,7 @@ version: "1.0" +workspace: + replacements: + wippy/dataflow: ../src vars: sqlite_file: ./.wippy/test.db postgres_host: 127.0.0.1 diff --git a/test/_index.yaml b/test/_index.yaml index 6df5748..3fb2e5f 100644 --- a/test/_index.yaml +++ b/test/_index.yaml @@ -188,6 +188,13 @@ entries: - name: wippy.migration:app_db value: app:db + - name: dep.wippy.security + kind: ns.dependency + meta: + description: Standard process policy groups required by framework services + component: wippy/security + version: ">=v0.4.2" + - name: dep.wippy.bootloader kind: ns.dependency meta: diff --git a/test/wippy.lock b/test/wippy.lock index ae4dc32..ea13319 100644 --- a/test/wippy.lock +++ b/test/wippy.lock @@ -3,29 +3,29 @@ directories: src: . modules: - name: wippy/agent - version: 0.4.14 - hash: ff47bf040857d520fbf9e576f1b2d8653516d36d340b82e7ecfa8ce1ed2fe9b4 + version: 0.4.17 + hash: f8b8ed9f8098e254f7b431ca9f5378672bb5deae33122b777d1714092bcca91a - name: wippy/bootloader - version: 0.3.13 - hash: 4f32d32b8a37a7c55b65e72242252eab2d53500591a83dca9787d742a65ff421 + version: 0.3.14 + hash: aa5e32807eba6a5ea0e5a224069a006288f58605c4542a444f5469caee862c58 - name: wippy/llm - version: 0.4.35 - hash: 0fe840b50d9ec7d1f4082dca0027f6009cd539aff5f0fd9234c70673232f6905 + version: 0.4.42 + hash: 173caaf535bafec0c184c93de63eca75e6281d87dfcfa460ec59982020812ad8 - name: wippy/migration - version: 0.3.17 - hash: 55834821cd2832f8582a52e98772810ba3bdfe91d4967262d3d69e6e9c2b2879 + version: 0.3.18 + hash: e00cad6706bb95f556aeab5318a04e55d3884c027b09f6340fc4e7d69c905a17 + - name: wippy/security + version: 0.4.2 + hash: 9020016afc9de612f5297d38ce99e8500c63a2986077e8b0e560d8636c506627 - name: wippy/session - version: 0.1.31 - hash: 9d9b2aca94b6857fea5e47bae014ff582a558a283dae1df5e997fe4fa948e671 + version: 0.4.2 + hash: 651bd603d4102702372a4c199bc9f165087a38f6a4ae9579a45bca78710e9df8 - name: wippy/terminal - version: 0.4.4 - hash: 11fcb6a64895364fa603000afd66e9889b5a711bc881f2b4fe5723cd9772df2a + version: 0.4.5 + hash: 028180c6044cdd9bbe15330255a4926770e02a291511930264a615f6388824f0 - name: wippy/test - version: 0.4.13 - hash: cfdbfc0a7cf05fa825d203b80ce92fbd24e03da00e2083efe21ff4266cb44608 + version: 0.4.17 + hash: 37a304cc20aa3005004255e9fda16835a94119db520c0c97e1db044c7844a229 - name: wippy/views - version: 0.5.6 - hash: 334151b0a36d1f26482e05c4076aa8f39187871376ac5c86120a5eaf51e91fc3 -replacements: - - from: wippy/dataflow - to: ../src + version: 0.5.10 + hash: a25c35b260e8f9c161f372aa8f5e80624af4db233f2b9de4a36d9015342f01fe diff --git a/wippy.yaml b/wippy.yaml index 026f893..f7ef97b 100644 --- a/wippy.yaml +++ b/wippy.yaml @@ -1,6 +1,6 @@ organization: wippy module: dataflow -version: 0.7.6 # x-release-please-version +version: 0.7.14 # x-release-please-version description: Workflow execution engine with visual flow designer support license: Apache-2.0 keywords: