Tools reference
Every MCP tool maps to one or two REST calls. Reads return an envelope with coverage; writes take idempotency_key and reason and end as executed or pending_approval. Generated from apps/mcp and KONTRAKT §11 (v1.0.0).
Read tools
#get_business_summary read
Financial summary of one business: revenue, costs (planned/reserved/actual kept separate), by category. Defaults to the current month.
get_business_summary(business_id, from?, to?) → FinancialSummary {revenue, costs, by_category[], planned/reserved/actual}
- Required scope
finances (observe)- Result
- envelope
{ data, coverage, fx?, provenance_summary?, request_id }
#get_business_context read
START HERE. One compact snapshot of a business (30d summary, free capital, forecast status, ads, top subscriptions, reservations, pending approvals, your passport, coverage). Replaces 6+ separate calls.
get_business_context(business_id) → {business, projects[], summary_30d, free_capital, forecast_status, subscriptions_top[], active_reservations[], pending_approvals[], my_passport,
- Required scope
finances (observe)- Result
- envelope
{ data, coverage, fx?, provenance_summary?, request_id }
#get_available_capital read
Free capital = cash_known − committed_costs − reserved_budgets − upcoming_subscriptions over the horizon. Check before proposing any spend.
get_available_capital(Scope, horizon_days?=30) → FreeCapital (§6)
- Required scope
finances (observe)- Result
- envelope
{ data, coverage, fx?, provenance_summary?, request_id }
#get_subscriptions read
Subscriptions in scope with cost allocations between businesses and capacity.
get_subscriptions(Scope, status?) → Subscription[] (z alokacjami)
- Required scope
finances (observe)- Result
- envelope
{ data, coverage, fx?, provenance_summary?, request_id }
#get_subscription_capacity read
Seats/slots of shared subscriptions: total, used, free, utilization, usage per workspace. Check for free capacity before proposing a new tool.
get_subscription_capacity(subscription_id? | Scope) → {subscription_id, unit, total, used, free, utilization, by_workspace[]}[]
- Required scope
finances (observe)- Result
- envelope
{ data, coverage, fx?, provenance_summary?, request_id }
#get_ad_performance readcoming soon
Ad spend per platform: planned / reserved / actual, results and budget pacing.
Ads integrations (Google Ads, Meta Ads) are coming soon: the tool works against planned budgets and reservations; live spend arrives once an Ads integration is connected.
get_ad_performance(business_id, from?, to?, platform?) → {platform, spend: P/R/A, results?, pacing?}[]
- Required scope
finances (observe)- Result
- envelope
{ data, coverage, fx?, provenance_summary?, request_id }
#get_cashflow_forecast read
Cash-flow forecast (7/30/90 days or custom range) with status list and reasons. Estimated components are labelled, never treat them as real data. Daily points are omitted unless include_points=true.
get_cashflow_forecast(Scope, horizon: 7|30|90 | {from,to}) → Forecast (§7)
- Required scope
finances (observe)- Result
- envelope
{ data, coverage, fx?, provenance_summary?, request_id }
#get_project_health read
Health of a business and its projects as a list of statuses with reasons (no scores).
get_project_health(business_id, project_id?) → {status[], reasons[]} (bez score)
- Required scope
finances (observe)- Result
- envelope
{ data, coverage, fx?, provenance_summary?, request_id }
#get_daily_brief read
Morning brief: last-24h highlights, alerts, upcoming costs (7d), approvals pending. Pass business_id unless you hold an `all` grant.
get_daily_brief(date?, business_id?) → {date, business_id|null, highlights[], alerts[], upcoming[], approvals_pending, goal_deviations[], agent_costs_30d: {agent_id, name, total_minor, currency}[] /* 0.7.1 */}
- Required scope
finances (observe)- Result
- envelope
{ data, coverage, fx?, provenance_summary?, request_id }
#get_goals read
Goals in scope (business/project/personal + your own agent goals) with `progress` computed from the ledger: current_value, target, gap, on_track, data_class. Analyse against the goal, not just whether revenue grows.
get_goals(Scope, include_archived?=false) → (Goal & {progress})[] (0.5.2, §2.17; tylko cele w zasięgu grantów)
- Required scope
goals (observe; onboarding = własny passport, zawsze dozwolone)- Result
- envelope
{ data, coverage, fx?, provenance_summary?, request_id }
#get_decision_outcomes read
Decision journal review: for each recorded decision, actual vs expected after the horizon (variance, met) and before/after change of ad_spend, revenue, net, roas, all with data_class. Use it to learn from your own history.
get_decision_outcomes(Scope, since?, limit?=20) → DecisionOutcome[] (0.5.2, §2.18; własne decyzje agenta)
- Required scope
decisions (observe)- Result
- envelope
{ data, coverage, fx?, provenance_summary?, request_id }
Write tools
Common parameters W = { idempotency_key, reason }; Scope = { business_id?, project_id?, personal? }; Money = { amount_minor, currency }.
#create_expense write
Record an ACTUAL one-off expense. Link reservation_id / fulfills_event_id when it consumes a reservation or fulfils a planned expense.
create_expense(W, Scope, Money, occurred_on, category, description) → FinancialEvent
- Required scope
expenses- Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)
#create_recurring_expense write
Create a subscription (recurring cost). Use either a single Scope or allocations[] (share_bps sum = 10000) for shared costs.
create_recurring_expense(W, Scope|allocations[], name, vendor, Money, billing_interval, next_charge_on, plan?, capacity?[]) → Subscription
- Required scope
subscriptions + expenses- Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)
#create_planned_expense write
Record a PLANNED one-off expense (intent). It enters forecasts and committed costs; it is never overwritten by the actual.
create_planned_expense(W, Scope, Money, expected_on, category, description) → FinancialEvent
- Required scope
planned_expenses- Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)
#reserve_budget write
Block capital for a purpose and period (BudgetReservation). Reduces free capital until period_end or release.
reserve_budget(W, Scope, Money, purpose, category, period_start, period_end) → BudgetReservation
- Required scope
budget_reserve- Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)
#allocate_shared_expense write
Replace the cost split of a shared subscription. Full list, share_bps must sum to 10000.
allocate_shared_expense(W, subscription_id, allocations: {Scope, method, share_bps}[]) → SubscriptionAllocation[]
- Required scope
allocations- Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)
#propose_ad_budget_change writecoming soon
Propose changing a DAILY ad budget. Always leaves an Approval record; executed at once only with ads:execute and within max_change_pct_per_day. The result carries `simulation` (impact on forecast/runway/free capital) so you and the owner see the effect before it lands. MVP writes the plan only, no ad platform is touched.
Ads integrations (Google Ads, Meta Ads) are coming soon: the tool works against planned budgets and reservations; live spend arrives once an Ads integration is connected.
propose_ad_budget_change(W, business_id, platform, campaign_id?, current: Money, proposed: Money) → Approval (zawsze; execute w MVP = zapis planu, bez wywołań platform)
- Required scope
ads (≥recommend)- Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)
#propose_goal write
Propose a goal (business goal or your own agent constraint, e.g. max ads spend). Always ends as pending_approval, the owner decides. Money targets in minor units.
propose_goal(W, Scope, metric, comparator, target_value, currency?, title?, deadline?, agent_id?) → Approval (zawsze 202, action=create_goal; §2.17)
- Required scope
goals (≥ recommend, wynik zawsze Approval)- Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)
#request_permission write
Ask the owner for a missing grant (scope × level × resource). Always ends as pending_approval; explain in `reason` what analysis is blocked without it.
request_permission(W, scope, level, resource, limits?, expires_at?) → Approval
- Required scope
-- Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)
#create_decision write
Decision journal: record what you are about to do, why, the expected impact (expected[] = measurable targets with horizon) and the data behind it, before a significant action. Reviewed against actuals after the horizon via get_decision_outcomes.
create_decision(W, Scope, title, rationale, expected_impact?, expected?[], action_ref?, refs?[]) → FinancialEvent(agent_action) (§2.18)
- Required scope
decisions- Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)
All signatures
// READ
get_business_summary(business_id, from?, to?) → FinancialSummary {revenue, costs, by_category[], planned/reserved/actual}
get_business_context(business_id) → {business, projects[], summary_30d, free_capital, forecast_status, subscriptions_top[], active_reservations[], pending_approvals[], my_passport,
recent_decisions: {id, title, decided_on, review: {status, met}, expected[], observed[]}[] /* 0.7.0, ≤5 */,
agent_costs_30d: {agent_id, name, total_minor, currency}[] /* 0.7.1 */, other_agents_activity: {agent_id, name, action, at, outcome}[] /* 0.7.1: 24 h, max 10, spec §31 */,
anomalies: {code: "revenue_drop"|"cost_spike"|"no_revenue_14d", message, amount_minor?, data_class}[] /* 0.7.0: revenue 7d vs poprzednie 7d < -30 %; koszt dnia > 3× mediana 30 d w kategorii; brak przychodu 14 dni */ }
get_available_capital(Scope, horizon_days?=30) → FreeCapital (§6)
get_subscriptions(Scope, status?) → Subscription[] (z alokacjami)
get_subscription_capacity(subscription_id? | Scope) → {subscription_id, unit, total, used, free, utilization, by_workspace[]}[]
get_ad_performance(business_id, from?, to?, platform?) → {platform, spend: P/R/A, results?, pacing?}[]
get_cashflow_forecast(Scope, horizon: 7|30|90 | {from,to}) → Forecast (§7)
get_project_health(business_id, project_id?) → {status[], reasons[]} (bez score)
get_daily_brief(date?, business_id?) → {date, business_id|null, highlights[], alerts[], upcoming[], approvals_pending, goal_deviations[], agent_costs_30d: {agent_id, name, total_minor, currency}[] /* 0.7.1 */}
get_goals(Scope, include_archived?=false) → (Goal & {progress})[] (0.5.2, §2.17; tylko cele w zasięgu grantów)
get_decision_outcomes(Scope, since?, limit?=20) → DecisionOutcome[] (0.5.2, §2.18; własne decyzje agenta)
get_onboarding(resource?) → Onboarding (0.5.2, spec §21; 0.6.1: `resource` = `business:<id>` | `project:<id>` zawęża sekcje do jednego zasobu) + MCP resource `operlance://onboarding` (markdown ≤ 2 000 tokenów, bez sekretów):
Onboarding = { about: string, my_passport: { agent: {id, name, kind}, grants: string[] /* "ads:execute@business:<id> ≤ 500 PLN/akcja, 10%/dzień" */, expires_at, token_expires_at },
businesses_accessible: {id, name, currency}[], forbidden: string[], limits: PassportGrant["limits"][], goals: Goal[],
concepts: { planned_vs_actual, reservations, decision_journal, approvals }: Record<string,string>, examples: {title, tools: string[]}[] }
// WRITE (W wymagane)
create_expense(W, Scope, Money, occurred_on, category, description) → FinancialEvent
create_recurring_expense(W, Scope|allocations[], name, vendor, Money, billing_interval, next_charge_on, plan?, capacity?[]) → Subscription
create_planned_expense(W, Scope, Money, expected_on, category, description) → FinancialEvent
reserve_budget(W, Scope, Money, purpose, category, period_start, period_end) → BudgetReservation
allocate_shared_expense(W, subscription_id, allocations: {Scope, method, share_bps}[]) → SubscriptionAllocation[]
propose_ad_budget_change(W, business_id, platform, campaign_id?, current: Money, proposed: Money) → Approval (zawsze; execute w MVP = zapis planu, bez wywołań platform)
request_permission(W, scope, level, resource, limits?, expires_at?) → Approval
create_decision(W, Scope, title, rationale, expected_impact?, expected?[], action_ref?, refs?[]) → FinancialEvent(agent_action) (§2.18)
propose_goal(W, Scope, metric, comparator, target_value, currency?, title?, deadline?, agent_id?) → Approval (zawsze 202, action=create_goal; §2.17)
// 0.7.0 (spec §29 What-if, §25 Agent Requests, §28 review)
simulate_scenario(Scope, horizon_days?=7|30|90, changes: Change[1..20]) → Simulation // READ (finances:observe + forecast:observe); bez zapisu, bez Idempotency-Key
Change = { kind: "add_expense", amount: Money, on?, category?, label? } | { kind: "add_recurring", amount: Money, billing_interval, interval_count?=1, next_charge_on?, label? }
| { kind: "cancel_subscription", subscription_id } | { kind: "change_ad_budget", platform, current: Money, proposed: Money /* budżety DZIENNE */, campaign_id?, from? } | { kind: "add_revenue", amount: Money, on?, label? }
ForecastSnapshot = { closing_cash_minor, min_cash_minor, net_minor, projected_in_minor, projected_out_minor, runway_days|null, free_capital_minor, break_even_on|null, statuses[] }
Simulation = { scope, currency, horizon_days, from, to, changes[], baseline: ForecastSnapshot, simulated: ForecastSnapshot,
delta: { closing_cash_minor, min_cash_minor, net_minor, runway_days|null, free_capital_minor, break_even_on: {from, to} },
unmodeled_minor /* część zmiany nieodwzorowana eventami, odjęta analitycznie */, data_class: "estimated", notes: string[] }
request_data(W, kind: "connect_integration"|"provide_data", details: { provider? /* connect */, field? /* provide, np. cash_snapshot */, why, impact_on_coverage? }) → Approval (202, action = kind)
get_decision_review(decision_id) → DecisionOutcome (§2.18; własne decyzje)
get_agent_economics(agent_id?, business_id?, from?, to?) → AgentEconomics (0.7.1, spec §32; agent: tylko własne)
// 1.0.0-rc.2 (Agentowy 66), experimental
get_personal_summary(from?, to?) → FinancialSummary dla personal (scope `finances:observe@personal`; brak grantu → scope_denied z what_to_do „poproś o grant @personal")
get_personal_forecast(horizon?: 7|30|90 | {from,to}, include_points?) → Forecast dla personal (jak wyżej)
plan_capacity(needs: {unit, amount, business_id}[1..10], W?) → { per_need: { need, covered_by: {subscription_id, free}|null, shortfall: int, suggestion: "allocate"|"upgrade"|"buy_new", est_cost_minor: int|null, proposed_allocations[] }[], proposals: Approval[] /* tylko z W: Approval allocate_shared_expense, nic nie kupuje */ }
AgentEconomics = { agent_id, period: {from,to}, currency, cost: { total_minor, runtime_minor /* agent_runtime z ledgera */, tools_minor /* alokacje subskrypcji z SubscriptionAllocation.agent_id */, by_category[] },
actions: { total, by_tool: Record<string,int>, denied: int }, approvals: { proposed, approved, rejected, expired, pending },
impact: { reviewed_decisions, met, missed, revenue_generated_minor|null, cost_saved_minor|null, data_class },
roi: { value: number|null, data_class: "estimated"|"unavailable", note } }
Source: KONTRAKT §11 (Polish, verbatim from the contract)
Serwer operlance, auth tokenem agenta. Każdy wynik: { data, coverage, provenance_summary?, request_id }. Błąd tool (0.6.1): isError: true + { code, message, what_to_do, retry_after_seconds?, details?, request_id } (code = kod z §9; what_to_do = jedno zdanie po angielsku, co agent ma zrobić, bez sekretów). Każdy write: parametry wspólne W = { idempotency_key: string, reason: string }; wynik { status: "executed"|"pending_approval", data?, approval_id? }. Błędy = kody z §9. Scope = { business_id?: string, project_id?: string, personal?: boolean }; Money = { amount_minor: int, currency: string }.
// READ
get_business_summary(business_id, from?, to?) → FinancialSummary {revenue, costs, by_category[], planned/reserved/actual}
get_business_context(business_id) → {business, projects[], summary_30d, free_capital, forecast_status, subscriptions_top[], active_reservations[], pending_approvals[], my_passport,
recent_decisions: {id, title, decided_on, review: {status, met}, expected[], observed[]}[] /* 0.7.0, ≤5 */,
agent_costs_30d: {agent_id, name, total_minor, currency}[] /* 0.7.1 */, other_agents_activity: {agent_id, name, action, at, outcome}[] /* 0.7.1: 24 h, max 10, spec §31 */,
anomalies: {code: "revenue_drop"|"cost_spike"|"no_revenue_14d", message, amount_minor?, data_class}[] /* 0.7.0: revenue 7d vs poprzednie 7d < -30 %; koszt dnia > 3× mediana 30 d w kategorii; brak przychodu 14 dni */ }
get_available_capital(Scope, horizon_days?=30) → FreeCapital (§6)
get_subscriptions(Scope, status?) → Subscription[] (z alokacjami)
get_subscription_capacity(subscription_id? | Scope) → {subscription_id, unit, total, used, free, utilization, by_workspace[]}[]
get_ad_performance(business_id, from?, to?, platform?) → {platform, spend: P/R/A, results?, pacing?}[]
get_cashflow_forecast(Scope, horizon: 7|30|90 | {from,to}) → Forecast (§7)
get_project_health(business_id, project_id?) → {status[], reasons[]} (bez score)
get_daily_brief(date?, business_id?) → {date, business_id|null, highlights[], alerts[], upcoming[], approvals_pending, goal_deviations[], agent_costs_30d: {agent_id, name, total_minor, currency}[] /* 0.7.1 */}
get_goals(Scope, include_archived?=false) → (Goal & {progress})[] (0.5.2, §2.17; tylko cele w zasięgu grantów)
get_decision_outcomes(Scope, since?, limit?=20) → DecisionOutcome[] (0.5.2, §2.18; własne decyzje agenta)
get_onboarding(resource?) → Onboarding (0.5.2, spec §21; 0.6.1: `resource` = `business:<id>` | `project:<id>` zawęża sekcje do jednego zasobu) + MCP resource `operlance://onboarding` (markdown ≤ 2 000 tokenów, bez sekretów):
Onboarding = { about: string, my_passport: { agent: {id, name, kind}, grants: string[] /* "ads:execute@business:<id> ≤ 500 PLN/akcja, 10%/dzień" */, expires_at, token_expires_at },
businesses_accessible: {id, name, currency}[], forbidden: string[], limits: PassportGrant["limits"][], goals: Goal[],
concepts: { planned_vs_actual, reservations, decision_journal, approvals }: Record<string,string>, examples: {title, tools: string[]}[] }
// WRITE (W wymagane)
create_expense(W, Scope, Money, occurred_on, category, description) → FinancialEvent
create_recurring_expense(W, Scope|allocations[], name, vendor, Money, billing_interval, next_charge_on, plan?, capacity?[]) → Subscription
create_planned_expense(W, Scope, Money, expected_on, category, description) → FinancialEvent
reserve_budget(W, Scope, Money, purpose, category, period_start, period_end) → BudgetReservation
allocate_shared_expense(W, subscription_id, allocations: {Scope, method, share_bps}[]) → SubscriptionAllocation[]
propose_ad_budget_change(W, business_id, platform, campaign_id?, current: Money, proposed: Money) → Approval (zawsze; execute w MVP = zapis planu, bez wywołań platform)
request_permission(W, scope, level, resource, limits?, expires_at?) → Approval
create_decision(W, Scope, title, rationale, expected_impact?, expected?[], action_ref?, refs?[]) → FinancialEvent(agent_action) (§2.18)
propose_goal(W, Scope, metric, comparator, target_value, currency?, title?, deadline?, agent_id?) → Approval (zawsze 202, action=create_goal; §2.17)
// 0.7.0 (spec §29 What-if, §25 Agent Requests, §28 review)
simulate_scenario(Scope, horizon_days?=7|30|90, changes: Change[1..20]) → Simulation // READ (finances:observe + forecast:observe); bez zapisu, bez Idempotency-Key
Change = { kind: "add_expense", amount: Money, on?, category?, label? } | { kind: "add_recurring", amount: Money, billing_interval, interval_count?=1, next_charge_on?, label? }
| { kind: "cancel_subscription", subscription_id } | { kind: "change_ad_budget", platform, current: Money, proposed: Money /* budżety DZIENNE */, campaign_id?, from? } | { kind: "add_revenue", amount: Money, on?, label? }
ForecastSnapshot = { closing_cash_minor, min_cash_minor, net_minor, projected_in_minor, projected_out_minor, runway_days|null, free_capital_minor, break_even_on|null, statuses[] }
Simulation = { scope, currency, horizon_days, from, to, changes[], baseline: ForecastSnapshot, simulated: ForecastSnapshot,
delta: { closing_cash_minor, min_cash_minor, net_minor, runway_days|null, free_capital_minor, break_even_on: {from, to} },
unmodeled_minor /* część zmiany nieodwzorowana eventami, odjęta analitycznie */, data_class: "estimated", notes: string[] }
request_data(W, kind: "connect_integration"|"provide_data", details: { provider? /* connect */, field? /* provide, np. cash_snapshot */, why, impact_on_coverage? }) → Approval (202, action = kind)
get_decision_review(decision_id) → DecisionOutcome (§2.18; własne decyzje)
get_agent_economics(agent_id?, business_id?, from?, to?) → AgentEconomics (0.7.1, spec §32; agent: tylko własne)
// 1.0.0-rc.2 (Agentowy 66), experimental
get_personal_summary(from?, to?) → FinancialSummary dla personal (scope `finances:observe@personal`; brak grantu → scope_denied z what_to_do „poproś o grant @personal")
get_personal_forecast(horizon?: 7|30|90 | {from,to}, include_points?) → Forecast dla personal (jak wyżej)
plan_capacity(needs: {unit, amount, business_id}[1..10], W?) → { per_need: { need, covered_by: {subscription_id, free}|null, shortfall: int, suggestion: "allocate"|"upgrade"|"buy_new", est_cost_minor: int|null, proposed_allocations[] }[], proposals: Approval[] /* tylko z W: Approval allocate_shared_expense, nic nie kupuje */ }
AgentEconomics = { agent_id, period: {from,to}, currency, cost: { total_minor, runtime_minor /* agent_runtime z ledgera */, tools_minor /* alokacje subskrypcji z SubscriptionAllocation.agent_id */, by_category[] },
actions: { total, by_tool: Record<string,int>, denied: int }, approvals: { proposed, approved, rejected, expired, pending },
impact: { reviewed_decisions, met, missed, revenue_generated_minor|null, cost_saved_minor|null, data_class },
roi: { value: number|null, data_class: "estimated"|"unavailable", note } }
#Transport zdalny MCP (1.0.0-rc.2, Zadanie 81/84)
- Ten sam serwer
operlance działa po stdio (paczka @operlance/mcp, token w env OPERLANCE_TOKEN) i po Streamable HTTP: POST <MCP_PUBLIC_URL>/mcp z nagłówkiem Authorization: Bearer agt_… per żądanie (stateless: brak sesji MCP, sessionIdGenerator wyłączony, odpowiedzi JSON), GET /health → {status:"ok", transport:"streamable-http", contract}; żadnych tokenów w env usługi. Staging: usługa mcp (Railway, apps/mcp/Dockerfile.mcp, port 8788) rozmawia z API przez sieć prywatną (OPERLANCE_API_URL=http://api.railway.internal:4000); adres publiczny tymczasowo domena Railway, docelowo mcp.operlance.app (Q20, PRODUCTION §2). - Auth i uprawnienia identyczne jak REST (token agenta → Passport;
allowed_cidrs widzą IP klienta z X-Forwarded-For); rate limit per token 120/min (na stagingu w pamięci procesu, docelowo rate_limits w DB); brak tokenu/zły token → HTTP 401 z {code:"unauthenticated"}; przekroczenie → 429 + Retry-After; błędy tool jak §11 (isError, what_to_do). CORS wyłączony (klienci MCP to procesy, nie przeglądarki); nagłówki z SECURITY §3; limit body 1 MB. - Klienci:
claude mcp add --transport http operlance <MCP_PUBLIC_URL>/mcp --header "Authorization: Bearer …"; Claude Desktop/Cursor: { "url": "…/mcp", "headers": { "Authorization": "Bearer …" } } (token wklejany przez Ownera z panelu, nigdy w repo/docs).
Source: KONTRAKT §10 (required scopes) (Polish, verbatim from the contract)
#10. Scopes i poziomy autonomii (spec §22-23)
Scopes (string): finances, subscriptions, ads, forecast, expenses, planned_expenses, budget_reserve, allocations, goals, decisions. Grant = scope × level × resource (all | business:<id> | project:<id> | personal) × limits. Zapis skrótowy: ads:execute@business:<id>.
| level | znaczenie |
|---|
observe | odczyt, analiza, raport |
recommend | observe + write tworzy Approval (202), nic nie jest wykonywane |
execute | recommend + write wykonywany od razu w limitach; ponad limit „per action" → Approval; ponad limit dzienny/miesięczny → 403 limit_exceeded |
- Konflikt zasobu między agentami (0.7.1, spec §31): write/propozycja dotycząca tego samego zasobu (kampania, subskrypcja, rezerwacja), dla którego istnieje
pending Approval innego agenta → 409 state_conflict z details.conflict {approval_id, agent_id, action} (AuditEntry denied, AuthzDecision.deny.code += state_conflict). Check konfliktu i INSERT Approval wykonywane w jednej transakcji (authorize dostaje tx), żeby dwie równoległe propozycje nie przeszły obie. - Limity dzienne/miesięczne liczą także kwoty OCZEKUJĄCYCH Approval agenta (
status=pending), ochrona przed podwójnym budżetowaniem (spec §31): usage = Σ wykonane akcje w oknie + Σ pending Approval z amount_minor w oknie; po rejected/expired/cancelled kwota wraca do puli; approved przechodzi z „pending” do „wykonane” bez podwójnego liczenia. limit_checked w AuditEntry rozbija usage na executed_minor i pending_minor. Odmowa limit_exceeded (0.6.1): AuditEntry denied z limit_checked[] (jeden wpis per sprawdzony limit: {limit_key, limit_minor|pct, usage_minor, attempted_minor, currency, window}). - Samoobsługowe dla każdego aktywnego agenta (zamierzone, 0.8.1):
GET /me, GET /agents/me, GET /agents/:id (własny), GET /agents/:id/passport (własny), POST /permission-requests, POST /data-requests, GET /approvals (własne): nie wymagają grantu, bo agent musi móc poznać własne uprawnienia i poprosić o więcej; zawsze audytowane. - Default deny. Brak grantu =
403 scope_denied (z audit denied). personal nigdy nie wynika z all, wymaga jawnego grantu @personal. - Grant na business obejmuje jego projekty. Grant wygasły (
expires_at) = brak grantu. ads:execute limit max_change_pct_per_day (np. 10 = ±10%).- Wymagany scope: czytanie podsumowań
finances:observe; create_expense→expenses; create_recurring_expense→subscriptions+expenses; create_planned_expense→planned_expenses; reserve_budget→budget_reserve; allocate_shared_expense→allocations; propose_ad_budget_change→ads (≥recommend); create_decision→decisions; get_decision_outcomes, get_decision_review→decisions (observe); simulate_scenario→finances+forecast (observe); request_data→dowolny aktywny agent (jak request_permission); get_goals, get_onboarding→goals (observe; onboarding = własny passport, zawsze dozwolone); propose_goal→goals (≥ recommend, wynik zawsze Approval).