What a program cannot do
By the end of this lesson, you can test sandbox restrictions, tool access, and execution-hook vetoes.
Test generated-code restrictions
Section titled “Test generated-code restrictions”The model writes the code. You did not read it before it ran, and on the next run it will be different code.
Build an Agent answered this question for one record: bind the tool, then prove the binding with a forged value. Code mode needs the same treatment, and the proofs are different because the thing you do not trust is different.
This whole lesson calls no model. All five checks cost nothing, and you can run them as often as you like.
Add a tool that writes
Section titled “Add a tool that writes”CC_FILE_CLAIM is registered as direct:
l_tool_id := uc_ai_tools_api.merge_tool_from_schema( p_tool_code => 'CC_FILE_CLAIM', p_description => 'File one claim against one shipment that broke the cold chain. ...', p_function_call => 'return cc_analyst_pkg.file_claim(:ARGUMENTS);', p_json_schema => json_object_t('{ ... }'), p_tags => apex_t_varchar2('coldchain'), p_code_mode_access => 'direct');Code mode splits the work three ways:
- the program computes over large data
- the model decides what to do with the small result
- the database decides whether the decision is allowed
Lesson 6 runs that split end to end. This lesson proves the first half of it: that a program cannot reach the write tool at all.
Check 1: a program cannot call it
Section titled “Check 1: a program cannot call it”The program below names the tool exactly, with the right arguments. Shipment 7 is
SHP-2047, and 150 minutes at 720 EUR is the claim the database agrees with:
const result = await callTool("CC_FILE_CLAIM", { shipment_id: 7, minutes_over: 150, amount_eur: 720 });{"error":"code execution failed: ORA-20504: Tool not found: CC_FILE_CLAIM", "hint":"Fix the program and call the code tool again. ..."}“Tool not found” is the answer from inside the program: for a program, this tool does not exist. The allow-list of the run answers before any of your code runs.
Check 2: the program cannot reach the database
Section titled “Check 2: the program cannot reach the database”The program runs in an Oracle Multilingual Engine (MLE) context of type PURE. A
PURE context has no access to SQL at all, and it removes every JavaScript API that
reaches the database. The script writes an uncommitted row first, then
runs this probe:
const probe = {};try { probe.require = typeof require("mle-js-oracledb"); } catch (e) { probe.require = "blocked"; }try { await import("mle-js-oracledb"); probe.import = "reached"; } catch (e) { probe.import = "blocked"; }probe.globals = [typeof oracledb, typeof session, typeof soda, typeof plsffi].join(",");const result = probe;{"require":"blocked","import":"blocked","globals":"undefined,undefined,undefined,undefined"} the uncommitted row of the caller is still pending: yesCOMMIT and ROLLBACK need no privilege. A sandbox built only on privileges
still lets generated code throw away the pending work of the caller. Removal of all
SQL is what stops that, and it is why PURE is the context to use.
Check 3: the hook fires on every inner call
Section titled “Check 3: the hook fires on every inner call”A hook is a package you write. UC AI finds before_tool_call in it by name, and you
attach the package to the session with one call:
uc_ai_agents_api.set_execution_hook('CC_HOOK_PKG');cc_hook_pkg counts calls to before_tool_call:
procedure before_tool_call( ... )asbegin g_calls := g_calls + 1;
if p_tool_code = g_block_tool then raise_application_error(-20999, 'Policy: ' || p_tool_code || ' is not allowed now.'); end if;end before_tool_call;Then one program reads every shipment:
3. A program reads every shipment in one call: {"shipments":24,"readings":1176} before_tool_call fired 25 times.Twenty-five. One for CC_LIST_SHIPMENTS and one for each of the 24
CC_GET_READINGS calls. UC AI does not call the hook one time for the program. It
calls the hook for every callTool inside it.
So authorization and auditing through this hook keep working in code mode. It also means that a hook which writes an audit row must be cheap. One tool call from the model can produce a hundred rows.
Check 4: a veto cannot be caught
Section titled “Check 4: a veto cannot be caught”Now the hook refuses one tool, and the program catches the error and continues:
let caught = "no";try { await callTool("CC_GET_READINGS", { shipment_id: 7 });} catch (e) { caught = "yes";}const result = { the_program_carried_on: caught };The program finished. The request did not:
ORA-20503: Invalid code mode tool call: vetoed by the before_tool_call hook:ORA-20999: Policy: CC_GET_READINGS is not allowed now.This is the one code-mode failure that is not handed to the model as data. UC AI records the veto and raises it after the sandbox returns. Catching the rejected promise in JavaScript does not prevent UC AI from raising the veto.
Check 5: the call budget
Section titled “Check 5: the call budget”One program can make 100 tool calls. The number is a constant, and you cannot change it:
let ok = 0, stoppedAt = null;for (let i = 0; i < 150; i++) { try { await callTool("CC_GET_LIMITS", {}); ok++; } catch (e) { stoppedAt = i; break; }}const result = { calls_that_worked: ok, stopped_at_call: stoppedAt };{"calls_that_worked":100,"stopped_at_call":100}A program can catch that error and keep calling. So UC AI stops a program for good after 1000 calls, whatever it catches.
Verification
Section titled “Verification”Every check above ran in the sandbox. No model was called, and nothing was written.
select count(*) as claims_filed from cc_claims;CLAIMS_FILED 0select code, code_mode_access from uc_ai_tools where code like 'CC\_%' escape '\' order by code;CODE CODE_MODE_ACCESSCC_FILE_CLAIM directCC_GET_LIMITS bothCC_GET_READINGS codeCC_LIST_SHIPMENTS codeAll five checks must hold:
- Check 1 returns
Tool not found: CC_FILE_CLAIM. - Check 2 returns
blocked,blocked, and four timesundefined, and the uncommitted row is still pending. - Check 3 prints 25.
- Check 4 raises
ORA-20503and names the hook in the message. - Check 5 stops at 100.
Lesson 6 gives CC_FILE_CLAIM to an agent, and the claims arrive there.
Key takeaways
Section titled “Key takeaways”code_mode_accessofdirectkeeps a tool out of every program, whatever the program writes.- A
PUREcontext removes all SQL, so generated code cannot read data and cannot commit or roll back your transaction. - Your tools keep every privilege and run in the transaction of the caller. The sandbox constrains the JavaScript, never the tools.
before_tool_callfires for eachcallToolinside a program. Keep it cheap.- A hook veto stops the request and cannot be caught by the program.
- The budget is 100 tool calls for one program, and it is not configurable.
Full reference: The security model.