When you send the user an updated version of a file you've already sent them,
give it a new filename with an incremented version number — report_v2.docx,
report_v3.docx, and so on. If you reuse the filename the user has already
received, the delivery event won't fire and they'll never get the update, even
though the file changed. A filename they haven't seen before is what triggers the
send.
So annotate each iteration with the next version number, and the user receives every version as its own attachment.
Example
report_v1.docx first version sent to the user
report_v2.docx the same file after "make the timeline more detailed"
report_v3.docx after "also add a budget table"
Run this — do not improvise
This capability's deterministic implementation is a RAPP single-file agent, linked beside this file as iterative_file_editing_agent.py and embedded as the fenced Python below (sha256 4c8da0bffae0ebcd…; a byte-exact copy is also vaulted in the capsule comment at the end of this file). On a host with sandbox execution, run the linked file directly — if it is missing, write the fence contents verbatim to iterative_file_editing_agent.py first:
python3 iterative_file_editing_agent.py '{"key": "value"}' # arguments as one JSON object
echo '{"key": "value"}' | python3 iterative_file_editing_agent.py # or on stdin
python3 iterative_file_editing_agent.py --tool # emit the JSON tool contract
Treat stdout as a tool result. If it reports missing or unresolved inputs, stop and collect them. If it returns steps, execute those steps in order exactly as returned; if it returns instructions, follow them with the supplied inputs. Otherwise use the result verbatim. Do not invent behavior beyond that output. On a host without code execution, treat the Parameters schema and the code below as the exact specification and never paraphrase a step. Never edit inside the generated markers; a converter-equipped host can instead restore the original file checksum-verified with the installed rapp-agent-converter/scripts/toast.py convert SKILL.md --to agent.
"""IterativeFileEditing -- Use this skill whenever you create or edit ANY file for the user in the Copilot Studio container — a document, spreadsheet, slide deck, code file, data export, anything. It keeps your work-in-progress durable and shows the user an updated version after every change, so the two of you refine the same file together across turns — the user sees real progress each round, earlier work is never lost, and neither of you has to start over. Apply it from the very first file you make.
Generated by the rapp skill from iterative-file-editing. The RCI capsule at the bottom of this file carries the full original; `toast.py convert` restores it byte-exact."""
import json
import re
import sys
try:
from agents.basic_agent import BasicAgent
except ImportError: # running OUTSIDE a brainstem -- stay executable anyway.
class BasicAgent: # noqa: D101 - minimal stand-in, same contract
def __init__(self, name=None, metadata=None):
if name:
self.name = name
if metadata:
self.metadata = metadata
def perform(self, **kwargs):
return "Not implemented."
def system_context(self):
return None
def to_tool(self):
return {"type": "function", "function": {
"name": self.name,
"description": self.metadata.get("description", ""),
"parameters": self.metadata.get("parameters", {})}}
# The procedural layer, verbatim from the source capability.
INSTRUCTIONS = 'When you send the user an updated version of a file you've **already** sent them,\ngive it a new filename with an incremented version number — `report_v2.docx`,\n`report_v3.docx`, and so on. If you reuse the filename the user has already\nreceived, the delivery event won't fire and they'll never get the update, even\nthough the file changed. A filename they haven't seen before is what triggers the\nsend.\n\nSo annotate each iteration with the next version number, and the user receives\nevery version as its own attachment.\n\n## Example\n```\nreport_v1.docx first version sent to the user\nreport_v2.docx the same file after "make the timeline more detailed"\nreport_v3.docx after "also add a budget table"\n```'
# Ordered commands lifted verbatim from the capability's own documentation.
STEPS = []
class IterativeFileEditingAgent(BasicAgent):
def __init__(self):
self.name = 'IterativeFileEditing'
self.metadata = {
"name": "IterativeFileEditing",
"description": "Use this skill whenever you create or edit ANY file for the user in the Copilot Studio container \u2014 a document, spreadsheet, slide deck, code file, data export, anything. It keeps your work-in-progress durable and shows the user an updated version after every change, so the two of you refine the same file together across turns \u2014 the user sees real progress each round, earlier work is never lost, and neither of you has to start over. Apply it from the very first file you make.",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
super().__init__(name=self.name, metadata=self.metadata)
def perform(self, **kwargs): # toaster:generated-perform
return json.dumps({"status": "ok", "instructions": INSTRUCTIONS,
"inputs": kwargs,
"note": "Prose-only capability: follow INSTRUCTIONS "
"with the given inputs."}, indent=2)
if __name__ == "__main__":
# echo '{"arg": "value"}' | python3 iterative_file_editing_agent.py
# python3 iterative_file_editing_agent.py '{"arg": "value"}'
# python3 iterative_file_editing_agent.py --tool # emit the JSON tool contract
_a = sys.argv[1:]
if _a and _a[0] == "--tool":
print(json.dumps(IterativeFileEditingAgent().to_tool(), indent=2))
else:
_raw = _a[0] if _a else (sys.stdin.read().strip() or "{}")
print(IterativeFileEditingAgent().perform(**json.loads(_raw)))
# rci-capsule:v1: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