Skip to content

feat(agency): Show item count and reversibility in confirmation dialog - #732

Open
marcelklehr wants to merge 3 commits into
mainfrom
feat/agency-confirmation-reversibility-itemcount
Open

marcelklehr wants to merge 3 commits into
mainfrom
feat/agency-confirmation-reversibility-itemcount

Conversation

@marcelklehr

@marcelklehr marcelklehr commented Nov 9, 2026 •

Copy link
Copy Markdown
Member

2026-11-09 - Marcel Klehr- Assistant agency confirmation with destructive batch action.webm

2026-11-09 - Marcel Klehr - Assistant agency confirmation with irreversible action.webm

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI (N/A)

… backends

Signed-off-by: Marcel Klehr <mklehr@gmx.net>
…to their own icons

Signed-off-by: Marcel Klehr <mklehr@gmx.net>
@marcelklehr
marcelklehr requested a review from julien-nc November 9, 2026 08:29
@coderabbitai

coderabbitai Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

Explain Change →

Walkthrough

Walkthrough


AgencyConfirmation now warns when an action is destructive or irreversible. Its footer displays separate conditional indicators for radius, destructive state, irreversible state, and affected-item count. The component sums finite item counts and shows the count only when the total exceeds one. Radius calculation skips missing and null values, maps unknown values to external, and returns no indicator when no action reports a radius.

Estimated code review effort:

Merge Risk: Low · up to 534ad

The dialog remains usable, but can understate action risks and affected-item totals. These bounded disclosure issues warrant correction or explicit owner acceptance before merging.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title names the two main changes in the changeset: showing item count and reversibility in the agency confirmation dialog. The raw summary confirms the AgencyConfirmation component now shows a cou…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description links two screen recordings of the assistant agency confirmation dialog, one for a destructive batch action and one for an irreversible action. It also includes an AI-usage disclosure.…
Full details: Title check

Explanation

The title names the two main changes in the changeset: showing item count and reversibility in the agency confirmation dialog. The raw summary confirms the AgencyConfirmation component now shows a count indicator for finite item_count values and an irreversible indicator for reversibility. The title is specific, follows the conventional commit style, and is a single sentence. It omits the destructive indicator and radius logic, which is acceptable.

Full details: Description check

Explanation

The description links two screen recordings of the assistant agency confirmation dialog, one for a destructive batch action and one for an irreversible action. It also includes an AI-usage disclosure. The changeset adds destructive and irreversible warnings, item count, and radius indicators to AgencyConfirmation.vue, so the description relates to the changes and is not off-topic.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/components/ChattyLLM/AgencyConfirmation.vue

typescript-eslint does not support TS 7.0.
Please see https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/#running-side-by-side-with-typescript-6.0 to run typescript-eslint using the TS 6 API.
See also typescript-eslint/typescript-eslint#10940 for tracking typescript-eslint's support for TS >=7.1

Oops! Something went wrong! :(

ESLint: 10.14.0

Error: typescript-eslint does not support TS 7.0.
at Object. (/.eslint-tmp/node_modules/typescript-eslint/dist/index.js:52:11)
at Module._compile (node:internal/modules/cjs/loader:1929:14)
at Object..js (node:internal/modules/cjs/loader:2060:10)
at Module.load (node:internal/modules/cjs/loader:1651:32)
at Module._load (node:internal/modules/cjs/loader:1443:12)
at wrapModuleLoad (node:internal/modules/cjs/loader:261:19)
at loadCJSModuleWithModuleLoad (node:internal/modules/esm/translators:372:15)
at ModuleWrap. (node:internal/modules/esm/translators:244:9)
at ModuleJob.run (node:internal/modules/esm/module_job:561:25)
at async node:internal/modules/esm/loader:647:26

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Review info

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8b21bab9-a6b0-486e-beb0-bfacafa2cbe0
Commits

Reviewing files that changed from the base of the PR and between 9e9a064 and 534adfb.

Files selected for processing: 1

  • src/components/ChattyLLM/AgencyConfirmation.vue

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment on lines +14 to +50
<div class="footer-left">
<NcButton v-if="radiusInfo"
variant="tertiary"
:title="radiusInfo.description"
:text="radiusInfo.label"
class="help">
<template #icon>
<component :is="radiusInfo.icon" :size="20" />
</template>
</NcButton>
<NcButton v-if="destructive.destructive"
variant="tertiary"
:title="destructive.description"
:aria-label="destructive.description"
class="help">
<template #icon>
<DeleteAlertOutlineIcon :size="20" />
</template>
</NcButton>
<NcButton v-if="irreversible.irreversible"
variant="tertiary"
:title="irreversible.description"
:aria-label="irreversible.description"
class="help">
<template #icon>
<RestoreAlertIcon :size="20" />
</template>
</NcButton>
<NcButton v-if="itemCount.itemCount > 1"
variant="tertiary"
:title="itemCount.description"
:text="String(itemCount.itemCount)"
class="help">
<template #icon>
<TallyMark5Icon :size="20" />
</template>
</NcButton>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Functional Correctness · Quick win

Supported by static analysis

Script executed:

sed -n '1,275p' src/components/ChattyLLM/AgencyConfirmation.vue
sed -n '1,85p' src/components/ChattyLLM/AgencyAction.vue
git diff 9e9a064a37de8ef5a55711e821a7ce0b11f34ea7 534adfbb0fbc50b966d035507f03ac5e423d6b39 -- src/components/ChattyLLM/AgencyConfirmation.vue

Repository: nextcloud/assistant

Length of output: 16132


Show the warning descriptions without hover.

Touch and keyboard users may not see the destructive or irreversible descriptions. The icon-only buttons also add tab stops that do nothing. Show the descriptions as text and use non-interactive indicators.

Suggested fix

 			<AgencyActions :actions="actions" />
+			<div v-if="destructive.destructive">
+				<DeleteAlertOutlineIcon :size="20" aria-hidden="true" />
+				{{ destructive.description }}
+			</div>
+			<div v-if="irreversible.irreversible">
+				<RestoreAlertIcon :size="20" aria-hidden="true" />
+				{{ irreversible.description }}
+			</div>
 			<div class="footer">
 				<div class="footer-left">
@@
-					<NcButton v-if="destructive.destructive"
-						variant="tertiary"
-						:title="destructive.description"
-						:aria-label="destructive.description"
-						class="help">
-						<template #icon>
-							<DeleteAlertOutlineIcon :size="20" />
-						</template>
-					</NcButton>
-					<NcButton v-if="irreversible.irreversible"
-						variant="tertiary"
-						:title="irreversible.description"
-						:aria-label="irreversible.description"
-						class="help">
-						<template #icon>
-							<RestoreAlertIcon :size="20" />
-						</template>
-					</NcButton>

Comment on lines +168 to +170
// actions that don't report a destructive state (older backends) are assumed to be non-destructive
for (const action of this.actions) {
if (action?.destructive !== false) {
if (action?.destructive !== undefined && action?.destructive !== null && action.destructive !== false) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Functional Correctness · Quick win

Supported by static analysis

Script executed:

grep -rn "destructive\|reversibility\|item_count\|impulse_radius" src lib --include=*.vue --include=*.js --include=*.php | head -50

Repository: nextcloud/assistant

Length of output: 2057


Script executed:

git diff 9e9a064a37de8ef5a55711e821a7ce0b11f34ea7 534adfbb0fbc50b966d035507f03ac5e423d6b39 -- src/components/ChattyLLM/AgencyConfirmation.vue
nl -ba src/components/ChattyLLM/AgencyConfirmation.vue | sed -n '1,280p'
rg -n -F -e 'sessionAgencyPendingActions' -e 'AgencyConfirmation' -e 'destructive' -e 'context_agent' -- src lib appinfo tests package.json composer.json || test "$?" -eq 1

Repository: nextcloud/assistant

Length of output: 19496


Script executed:

rg -n -F -e 'improveAgencyActionNames' -e 'getAgencyPendingActions' -e 'setAgencyPendingActions' -e 'agencyPendingActions' -e 'agencyAnswered' -e 'confirmAgency' -- lib src tests || test "$?" -eq 1
nl -ba lib/Controller/ChattyLLMController.php | sed -n '570,735p'
nl -ba src/components/ChattyLLM/AgencyActions.vue | sed -n '1,240p'
nl -ba src/components/ChattyLLM/ChattyLLMInputForm.vue | sed -n '180,225p;610,645p;825,890p;1470,1510p'
rg -n -F -e 'context_agent' -e 'nextcloud' -- appinfo/info.xml composer.json README.md | sed -n '1,160p'

Repository: nextcloud/assistant

Length of output: 24945


Web query:

site:github.com/nextcloud/context_agent "destructive" "impulse_radius" pending actions older backend

Result:

Sources consulted:
- GitHub - nextcloud/context_agent · GitHub — https://github.com/nextcloud/context_agent
- Releases - Nextcloud Context Agent - Apps - App Store - Nextcloud — https://apps.nextcloud.com/apps/context_agent/releases?platform=31
- context_agent/CHANGELOG.md at main · nextcloud/context_agent · GitHub — https://github.com/nextcloud/context_agent/blob/main/CHANGELOG.md
- Issues · nextcloud/context_agent · GitHub — https://github.com/nextcloud/context_agent/issues
- …and 29 more

Script executed:

nl -ba lib/Controller/ChattyLLMController.php | sed -n '155,215p'
nl -ba lib/Listener/ChattyLLMTaskListener.php | sed -n '110,185p'
nl -ba src/components/ChattyLLM/AgencyAction.vue | sed -n '1,220p'
nl -ba src/components/ChattyLLM/ChattyLLMInputForm.vue | sed -n '1620,1685p'
python3 - <<'PY'
import json, urllib.request
url = 'https://api.github.com/repos/nextcloud/context_agent/git/trees/2.8.0?recursive=1'
try:
    with urllib.request.urlopen(urllib.request.Request(url, headers={'User-Agent':'evidence-inspection'}), timeout=20) as r:
        data = json.load(r)
    print('TREE SHA:',data.get('sha'))
    for entry in data.get('tree', []):
        p=entry['path']
        if p.endswith('.py') and any(w in p.lower() for w in ['tool','agent','task','confirmation']):
            print(p)
except Exception as e:
    print('FETCH ERROR:',type(e).__name__,str(e))
PY

Repository: nextcloud/assistant

Length of output: 12445


Web query:

github nextcloud context_agent "pending_actions" "delete" 2.8.0 tool confirmation

Result:

Sources consulted:
- context_agent/CHANGELOG.md at main · nextcloud/context_agent · GitHub — https://github.com/nextcloud/context_agent/blob/main/CHANGELOG.md
- Releases - Nextcloud Context Agent - Apps - App Store - Nextcloud — https://apps.nextcloud.com/apps/context_agent/releases?platform=31
- Releases - Nextcloud Context Agent - Apps - App Store - Nextcloud — https://apps.nextcloud.com/apps/context_agent/releases?platform=34
- …and 45 more

Script executed:

python3 - <<'PY'
import json, urllib.request
base='https://api.github.com/repos/nextcloud/context_agent/'
def get(path):
    with urllib.request.urlopen(urllib.request.Request(base+path, headers={'User-Agent':'evidence-inspection'}), timeout=30) as r: return json.load(r)
try:
    tags=get('tags?per_page=100')
    print('Relevant tags:', [(t['name'],t['commit']['sha']) for t in tags if any(v in t['name'] for v in ('2.8','2.9','2.10'))])
    for t in tags:
        if '2.8.0' in t['name']:
            data=get('git/trees/'+t['commit']['sha']+'?recursive=1')
            print('TREE',t['name'],data.get('sha'),'truncated',data.get('truncated'))
            for entry in data.get('tree',[]):
                p=entry['path']
                if p.endswith('.py') and any(w in p.lower() for w in ['tool','agent','task','confirmation']): print(p)
            break
except Exception as e:
    print('FETCH ERROR:',type(e).__name__,str(e))
PY

Repository: nextcloud/assistant

Length of output: 1636


Script executed:

python3 - <<'PY'
import urllib.request
root='https://raw.githubusercontent.com/nextcloud/context_agent/99936dd8c518b164aae8bdb12e965a640fe6f1fc/'
paths=['ex_app/lib/agent.py','ex_app/lib/all_tools/files.py','ex_app/lib/all_tools/forms.py','ex_app/lib/all_tools/lib/decorator.py','ex_app/lib/tools.py']
for p in paths:
    try:
        with urllib.request.urlopen(root+p, timeout=25) as r: lines=r.read().decode().splitlines()
        print('\n###',root+p)
        hits=[i for i,l in enumerate(lines) if any(w in l.lower() for w in ['pending','interrupt','confirm','delete_','destructive','reversibility','actions'])]
        spans=[]
        for i in hits:
            a=max(0,i-5); b=min(len(lines),i+11)
            if spans and a<=spans[-1][1]: spans[-1]=(spans[-1][0],max(b,spans[-1][1]))
            else: spans.append((a,b))
        for a,b in spans:
            print('---')
            for i in range(a,b): print(f'{i+1:4} {lines[i]}')
    except Exception as e: print('FETCH ERROR',p,type(e).__name__,str(e))
PY

Repository: nextcloud/assistant

Length of output: 8967


Script executed:

python3 - <<'PY'
import urllib.request
root='https://raw.githubusercontent.com/nextcloud/context_agent/99936dd8c518b164aae8bdb12e965a640fe6f1fc/'
for p, ranges in {
'ex_app/lib/agent.py':[(1,125),(175,235),(235,298)],
'ex_app/lib/all_tools/files.py':[(320,370)],
'pyproject.toml':[(1,120)],
}.items():
    try:
        with urllib.request.urlopen(root+p, timeout=25) as r: lines=r.read().decode().splitlines()
        print('\n###',root+p)
        for a,b in ranges:
            for i in range(a-1,min(b,len(lines))): print(f'{i+1:4} {lines[i]}')
    except Exception as e: print('FETCH ERROR',p,type(e).__name__,str(e))
PY

Repository: nextcloud/assistant

Length of output: 14889


Web query:

Langchain Python ToolCall TypedDict name args id type optional langchain_core messages tool tool_calls

Result:

Sources consulted:
- ToolCall | langchain_core | LangChain Reference — https://reference.langchain.com/python/langchain-core/messages/tool/ToolCall
- ToolCall | langchain_core | LangChain Reference — https://reference.langchain.com/python/langchain-core/messages/content/ToolCall
- langchain/openwiki/tools.md at master · langchain-ai/langchain · GitHub — https://github.com/langchain-ai/langchain/blob/master/openwiki/tools.md
- langchain/libs/core/langchain_core/messages/content.py at master · langchain-ai/langchain · GitHub — https://github.com/langchain-ai/langchain/blob/master/libs/core/langchain_core/messages/content.py
- Tool Calling with LangChain — https://www.langchain.com/blog/tool-calling-with-langchain
- …and 26 more

Script executed:

python3 - <<'PY'
import urllib.request
root='https://raw.githubusercontent.com/nextcloud/context_agent/99936dd8c518b164aae8bdb12e965a640fe6f1fc/'
for p, needles in {
'ex_app/lib/nc_model.py':['tool_calls','tool_call','AIMessage'],
'ex_app/lib/graph.py':['dangerous_tools','interrupt','tool_calls','ToolNode'],
'ex_app/lib/all_tools/lib/decorator.py':['dangerous_tool'],
'ex_app/lib/tools.py':['dangerous','get_tools'],
'appinfo/info.xml':['nextcloud']
}.items():
    try:
        with urllib.request.urlopen(root+p, timeout=25) as r: lines=r.read().decode().splitlines()
        print('\n###',root+p)
        spans=[]
        for i,l in enumerate(lines):
            if any(n in l for n in needles):
                a=max(0,i-8); b=min(len(lines),i+15)
                if spans and a<=spans[-1][1]: spans[-1]=(spans[-1][0],max(b,spans[-1][1]))
                else: spans.append((a,b))
        for a,b in spans:
            print('---')
            for i in range(a,b): print(f'{i+1:4} {lines[i]}')
    except Exception as e: print('FETCH ERROR',p,type(e).__name__,str(e))
PY

Repository: nextcloud/assistant

Length of output: 19980


Warn when destructiveness is unknown.

With Context Agent 2.8.0, a pending file deletion has no destructive or reversibility field. The dialog therefore shows an info card without the content-loss indicator. Show an unknown-risk warning when destructiveness is missing instead of treating the action as non-destructive.

Comment on lines +202 to +215
itemCount() {
// determine how many items are affected by the pending actions
let itemCount = 0
for (const action of this.actions) {
if (Number.isFinite(action?.item_count)) {
itemCount += action.item_count
}
}
return {
itemCount,
// TRANSLATORS Additional description for pending AI agent actions to be confirmed by the user
description: n('assistant', '%n item is affected by these actions.', '%n items are affected by these actions.', itemCount),
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Functional Correctness · Quick win

Supported by static analysis

Script executed:

git diff 9e9a064a37de8ef5a55711e821a7ce0b11f34ea7 534adfbb0fbc50b966d035507f03ac5e423d6b39 -- src/components/ChattyLLM/AgencyConfirmation.vue
nl -ba src/components/ChattyLLM/AgencyConfirmation.vue | sed -n '1,280p'
rg -n -F -e 'item_count' -e 'AgencyConfirmation' -- src tests lib appinfo composer.json package.json || test "$?" -eq 1

Repository: nextcloud/assistant

Length of output: 16673


Script executed:

rg -n -F -e 'sessionAgencyPendingActions' -e 'pending_actions' -e 'pendingActions' -e 'context_agent' -- src lib tests appinfo openapi.json composer.json || test "$?" -eq 1
nl -ba src/components/ChattyLLM/ChattyLLMInputForm.vue | sed -n '180,225p'
nl -ba src/components/ChattyLLM/AgencyActions.vue | sed -n '1,220p'
git remote -v

Repository: nextcloud/assistant

Length of output: 7340


Web query:

Nextcloud context_agent item_count pending actions reversibility item_count action contract source

Result:

Sources consulted:
- feat: Add 'reversibility' and 'item count' to action classification by marcelklehr · Pull Request #298 · nextcloud/context_agent · GitHub — GitHub pull request 298 in nextcloud/context_agent (link omitted to avoid creating a cross-reference)
- App: Context Agent (context_agent) — Nextcloud 35 Administration Manual — https://docs.nextcloud.com/server/stable/admin_manual/ai/app_context_agent.html
- Releases - Nextcloud Context Agent - Apps - App Store - Nextcloud — https://apps.nextcloud.com/apps/context_agent/releases?platform=31
- Pull requests · nextcloud/context_agent · GitHub — https://github.qzink.me/nextcloud/context_agent/pulls
- Issues · nextcloud/context_agent · GitHub — https://github.com/nextcloud/context_agent/issues
- …and 12 more

Script executed:

nl -ba lib/Listener/ChattyLLMTaskListener.php | sed -n '110,190p'
nl -ba lib/Controller/ChattyLLMController.php | sed -n '590,630p;670,725p'
rg -n -F 'function improveAgencyActionNames' lib/Controller/ChattyLLMController.php
nl -ba src/components/ChattyLLM/ChattyLLMInputForm.vue | sed -n '610,645p;1470,1510p'
nl -ba openapi.json | sed -n '60,100p;205,250p'
curl -fsSL 'https://api.github.com/repos/nextcloud/context_agent/git/trees/v2.11.0?recursive=1' | python3 -c 'import json,sys; d=json.load(sys.stdin); print("\n".join(x["path"] for x in d.get("tree",[]) if x["path"].endswith(".py") and ("classif" in x["path"] or "agent" in x["path"] or "tool" in x["path"])))'

Repository: nextcloud/assistant

Length of output: 18770


Script executed:

nl -ba lib/Controller/ChattyLLMController.php | sed -n '160,225p'
for path in ex_app/lib/agent.py ex_app/lib/tools.py ex_app/lib/all_tools/lib/impulse.py ex_app/lib/all_tools/lib/decorator.py ex_app/lib/all_tools/mcp.py; do
  printf '\n--- context_agent v2.11.0 %s ---\n' "$path"
  curl -fsSL "https://raw.githubusercontent.com/nextcloud/context_agent/v2.11.0/$path" | nl -ba | grep -C 9 -E 'item_count|pending_actions|json.dumps|classif' || test "$?" -eq 1
done

Repository: nextcloud/assistant

Length of output: 24027


Script executed:

for spec in 'ex_app/lib/agent.py:1:100' 'ex_app/lib/all_tools/lib/impulse.py:315:344' 'ex_app/lib/all_tools/lib/impulse.py:407:445' 'ex_app/lib/all_tools/files.py:1:180' 'ex_app/lib/all_tools/lib/files.py:1:220'; do
  IFS=: read -r path first last <<< "$spec"
  printf '\n--- context_agent v2.11.0 %s:%s-%s ---\n' "$path" "$first" "$last"
  curl -fsSL "https://raw.githubusercontent.com/nextcloud/context_agent/v2.11.0/$path" | nl -ba | sed -n "${first},${last}p"
done
curl -fsSL 'https://api.github.com/repos/nextcloud/context_agent/git/trees/v2.11.0?recursive=1' | python3 -c 'import json,sys; d=json.load(sys.stdin); print("\n".join(x["path"] for x in d.get("tree",[]) if x["path"].endswith(".py") and ("graph" in x["path"] or "classification" in x["path"])))'

Repository: nextcloud/assistant

Length of output: 27178


Script executed:

printf '%s\n' '--- context_agent v2.11.0 ex_app/lib/graph.py ---'
curl -fsSL 'https://raw.githubusercontent.com/nextcloud/context_agent/v2.11.0/ex_app/lib/graph.py' | nl -ba | grep -C 18 -E 'classifications|annotate|classif|tool_calls'

Repository: nextcloud/assistant

Length of output: 9483


Script executed:

nl -ba lib/Service/ChatService.php | sed -n '480,570p'
cat appinfo/info.xml | sed -n '1,100p'
rg -n -F -e 'ContextAgentInteraction' -e 'context_agent' -e 'taskProcessingManager' -- lib/TaskProcessing lib/Service/ChatService.php lib/Settings/Admin.php

Repository: nextcloud/assistant

Length of output: 13608


Web query:

Nextcloud ContextAgentInteraction task type actions output JSON array task provider item_count fields contract

Result:

Sources consulted:
- Task Processing — Nextcloud 35 Developer Manual — https://docs.nextcloud.com/server/stable/developer_manual/digging_deeper/task_processing.html
- Releases - Nextcloud Context Agent - Apps - App Store - Nextcloud — https://apps.nextcloud.com/apps/context_agent/releases?platform=31
- nextcloud-server: lib/public/TaskProcessing/TaskTypes/ContextAgentInteraction.php Source File - doxygen documentation | Fossies Dox — https://fossies.org/dox/nextcloud-server-35.0.1/ContextAgentInteraction_8php_source.html
- …and 30 more

Script executed:

printf '%s\n' '--- Nextcloud server stable35 ContextAgentInteraction ---'
curl -fsSL 'https://raw.githubusercontent.com/nextcloud/server/stable35/lib/public/TaskProcessing/TaskTypes/ContextAgentInteraction.php' | nl -ba | sed -n '15,135p'
nl -ba lib/Service/ChatService.php | sed -n '620,650p;840,895p'
nl -ba src/components/ChattyLLM/AgencyAction.vue | sed -n '1,150p'

Repository: nextcloud/assistant

Length of output: 10611


Show when the total item count is unknown.

If one action omits item_count, the dialog treats it as zero. A batch with another count of 5 then reports exactly 5 affected items, even if the omitted action affects more. Mark the total as unknown instead of presenting the partial sum as exact.


Minor · Functional Correctness · Quick win

Supported by static analysis

Script executed:

nl -ba src/components/ChattyLLM/AgencyConfirmation.vue | sed -n '1,290p'
git diff 9e9a064a37de8ef5a55711e821a7ce0b11f34ea7 534adfbb0fbc50b966d035507f03ac5e423d6b39 -- src/components/ChattyLLM/AgencyConfirmation.vue
rg -n -F -e 'item_count_exact' -e 'item_count' -e 'AgencyConfirmation' -- src tests composer.json info.xml appinfo || test "$?" -eq 1

Repository: nextcloud/assistant

Length of output: 16726


Web query:

Nextcloud context_agent 2.11.0 item_count_exact item_count limit classification source code

Result:

Sources consulted:
- feat: Add 'reversibility' and 'item count' to action classification by marcelklehr · Pull Request #298 · nextcloud/context_agent · GitHub — GitHub pull request 298 in nextcloud/context_agent (link omitted to avoid creating a cross-reference)
- Releases - Nextcloud Context Agent - Apps - App Store - Nextcloud — https://apps.nextcloud.com/apps/context_agent/releases?platform=31
- Nextcloud Context Agent - Apps - App Store - Nextcloud — https://apps.nextcloud.com/apps/context_agent
- Bug Report: HTTP 429 Too Many Requests When Scheduling Tasks in Nextcloud Task Processing API - ℹ️ Support - Nextcloud community — https://help.nextcloud.com/t/bug-report-http-429-too-many-requests-when-scheduling-tasks-in-nextcloud-task-processing-api/251770
- context_agent/pyproject.toml at main · nextcloud/context_agent · GitHub — https://github.com/nextcloud/context_agent/blob/main/pyproject.toml
- …and 13 more

Script executed:

rg -n -F -e 'sessionAgencyPendingActions' -e 'agencyPendingActions' -e 'agency_pending_actions' -- src lib tests || test "$?" -eq 1
nl -ba src/components/ChattyLLM/ChattyLLMInputForm.vue | sed -n '180,220p'
python3 - <<'PY'
import json, urllib.request
base='https://api.github.com/repos/nextcloud/context_agent'
for endpoint in ['/git/ref/tags/v2.11.0','/git/ref/tags/2.11.0','/pulls/298/files?per_page=100']:
    try:
        req=urllib.request.Request(base+endpoint,headers={'User-Agent':'code-review'})
        data=json.load(urllib.request.urlopen(req,timeout=20))
        if isinstance(data,list):
            for item in data:
                print('FILE',item['filename'])
        else:
            print(endpoint,json.dumps(data.get('object')))
    except Exception as e:
        print(endpoint,'ERROR',e)
PY

Repository: nextcloud/assistant

Length of output: 6401


Web query:

site:github.com/nextcloud/context_agent "item_count_exact" "item_count" count limit 2.11.0

Result:

Sources consulted:
- Releases - Nextcloud Context Agent - Apps - App Store - Nextcloud — https://apps.nextcloud.com/apps/context_agent/releases?platform=31
- GitHub - nextcloud/context_agent · GitHub — https://github.com/nextcloud/context_agent
- Nextcloud Context Agent - Apps - App Store - Nextcloud — https://apps.nextcloud.com/apps/context_agent
- context_agent/CHANGELOG.md at main · nextcloud/context_agent · GitHub — https://github.com/nextcloud/context_agent/blob/main/CHANGELOG.md
- context_agent/pyproject.toml at main · nextcloud/context_agent · GitHub — https://github.com/nextcloud/context_agent/blob/main/pyproject.toml
- …and 13 more

Script executed:

python3 - <<'PY'
import urllib.request
root='https://raw.githubusercontent.com/nextcloud/context_agent/5fdcad6cd0a5517443ac612c59928a69d1e6fa2b/'
paths=['ex_app/lib/agent.py','ex_app/lib/graph.py','ex_app/lib/tools.py','ex_app/lib/all_tools/files.py','ex_app/lib/all_tools/lib/files.py','ex_app/lib/all_tools/lib/impulse.py','ex_app/lib/main.py','tests/test_confirmation_policy.py']
terms=['item_count_exact','item_count','count_limit','pending_actions']
for path in paths:
    try:
        req=urllib.request.Request(root+path,headers={'User-Agent':'code-review'})
        lines=urllib.request.urlopen(req,timeout=20).read().decode().splitlines()
    except Exception as e:
        print('ERROR',path,e); continue
    hits=[i for i,s in enumerate(lines) if any(t in s for t in terms)]
    print('\n###',root+path,'lines',len(lines),'hits',len(hits))
    show=set()
    for i in hits:
        show.update(range(max(0,i-5),min(len(lines),i+7)))
    for i in sorted(show):
        print(f'{i+1:4} {lines[i]}')
PY
rg -n -F -e 'setAgencyPendingActions' -e 'improveAgencyActionNames' -e 'sessionAgencyPendingActions' -- lib src/components/ChattyLLM/ChattyLLMInputForm.vue
nl -ba lib/Controller/ChattyLLMController.php | sed -n '580,720p'

Repository: nextcloud/assistant

Length of output: 34534


Script executed:

python3 - <<'PY'
import urllib.request
root='https://raw.githubusercontent.com/nextcloud/context_agent/5fdcad6cd0a5517443ac612c59928a69d1e6fa2b/'
for path in ['ex_app/lib/graph.py','ex_app/lib/agent.py','ex_app/lib/main.py']:
    lines=urllib.request.urlopen(urllib.request.Request(root+path,headers={'User-Agent':'code-review'}),timeout=20).read().decode().splitlines()
    print('\n###',root+path)
    terms=['annotate(', 'pending', 'tool_calls', 'actions', 'interrupt', 'output']
    hits=[i for i,s in enumerate(lines) if any(t in s for t in terms)]
    show=set()
    for i in hits: show.update(range(max(0,i-4),min(len(lines),i+6)))
    for i in sorted(show): print(f'{i+1:4} {lines[i]}')
PY
nl -ba lib/Listener/ChattyLLMTaskListener.php | sed -n '105,195p'
nl -ba lib/Controller/ChattyLLMController.php | sed -n '165,215p'
nl -ba src/components/ChattyLLM/ChattyLLMInputForm.vue | sed -n '610,650p;1470,1510p'

Repository: nextcloud/assistant

Length of output: 26886


Show capped counts as lower bounds.

When Context Agent caps a folder count, this dialog presents the cap as an exact total. A folder with more than 11 items can therefore appear to affect only 11. Check item_count_exact and display an inexact count as a lower bound.

// actions that don't report a destructive state (older backends) are assumed to be non-destructive
for (const action of this.actions) {
if (action?.destructive !== false) {
if (action?.destructive !== undefined && action?.destructive !== null && action.destructive !== false) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with CodeRabbit 😁 . Let's clarify what should be displayed when the fields are missing.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fields are only missing when using context_agent < v2.9.0 and in that case the behavior now is the same as it was before.

// determine how many items are affected by the pending actions
let itemCount = 0
for (const action of this.actions) {
if (Number.isFinite(action?.item_count)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we cast to int? Right now it seems possible to render float counts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants