Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
2a31d0e
refactor(groups): extract GroupAttachmentBinder from GroupConversatio…
ginccc Aug 1, 2026
fe2edde
refactor(groups): extract GroupContextBuilder from GroupConversationS…
ginccc Aug 1, 2026
727d516
refactor(groups): extract GroupSigningGuard from GroupConversationSer…
ginccc Aug 1, 2026
8b76d74
fix(groups): key-rotation-safe signature verification
ginccc Aug 1, 2026
741bc3f
refactor(groups): extract MemberTurnExecutor from GroupConversationSe…
ginccc Aug 1, 2026
02d3760
refactor(groups): extract PhaseExecutionEngine from GroupConversation…
ginccc Aug 1, 2026
7de245a
refactor(groups): extract TaskForceEngine from GroupConversationService
ginccc Aug 1, 2026
338ddd4
docs(groups): fix stale PhaseExecutionEngine comment, log review of s…
ginccc Aug 1, 2026
5071acd
refactor(groups): extract GroupHitlCoordinator from GroupConversation…
ginccc Aug 1, 2026
8405935
refactor(groups): extract GroupLifecycleOps from GroupConversationSer…
ginccc Aug 1, 2026
118b310
chore(groups): R1 step 9 verification - facade finalized, no further …
ginccc Aug 1, 2026
2ae7e5d
feat(groups): wire group discussions into graceful shutdown
ginccc Aug 1, 2026
710a562
refactor(orchestrator): extract ToolContextBudget from AgentOrchestrator
ginccc Aug 1, 2026
0809465
feat(orchestrator): introduce the ToolSourceProvider SPI
ginccc Aug 1, 2026
105ce2f
refactor(orchestrator): extract HttpCallToolsProvider, first SPI prov…
ginccc Aug 1, 2026
25dd1b6
refactor(orchestrator): extract McpToolsProvider
ginccc Aug 1, 2026
82fd81b
feat(orchestrator): add A2AToolsProvider, not yet wired
ginccc Aug 1, 2026
76d9e52
refactor(orchestrator): extract DynamicAgentToolsProvider + ToolObjec…
ginccc Aug 2, 2026
a8cc233
refactor(orchestrator): extract ContextualToolsProvider
ginccc Aug 2, 2026
15c0299
fix(review): critical review pass over Wave R — 8 defects, docs corre…
ginccc Aug 2, 2026
b499aa7
fix(review): PR #626 automated-review findings — 6 defects, 2 declined
ginccc Aug 2, 2026
40c7a06
Merge origin/main into refactor/group-service-split
ginccc Aug 2, 2026
bcea36f
refactor(orchestrator): BuiltinToolsProvider + close the three SPI gaps
ginccc Aug 2, 2026
9109084
refactor(orchestrator): buildToolSetup now iterates providers
ginccc Aug 2, 2026
fc80f34
refactor(orchestrator): extract ToolApprovalGateSupport
ginccc Aug 2, 2026
b6e4c6a
refactor(orchestrator): introduce IAgentOrchestrator
ginccc Aug 2, 2026
956aa76
refactor(orchestrator): extract ToolLoopRunner + ToolLoopResumer
ginccc Aug 2, 2026
f0f104c
refactor(conversation): extract ConversationHitlService
ginccc Aug 2, 2026
09c365e
refactor(conversation): extract ConversationStepRunner — Wave R complete
ginccc Aug 2, 2026
135ab46
fix(review): Wave R branch review + PR nitpicks
ginccc Aug 2, 2026
f92fa2c
docs(groups): explain the remaining useless-parameter findings in place
ginccc Aug 2, 2026
7114358
fix(groups): DEBATE opposingArguments team-filter bug (V6(a))
ginccc Aug 3, 2026
f0c3d0c
feat(groups): LiveDiscussionRegistry (Wave 0, F1)
ginccc Aug 3, 2026
58344da
feat(groups): Speaker-level ResumePoint (Wave 0, F2)
ginccc Aug 3, 2026
e9dd3a7
feat(groups): DecisionRecord (Wave 0, F3)
ginccc Aug 3, 2026
74749ca
feat(groups): Transcript entry types + visibility matrix (Wave 0, F4)
ginccc Aug 3, 2026
e805225
feat(groups): GroupCostLedger (Wave 0, F5)
ginccc Aug 3, 2026
4ef2b96
feat(groups): Paused-document schema versioning (Wave 0, F6)
ginccc Aug 3, 2026
3aae80c
feat(groups): Group cost ceiling + attribution (I1)
ginccc Aug 3, 2026
743e9de
docs(orchestrator): correct four Javadocs left stale by R2's rewiring
ginccc Aug 3, 2026
3ada824
fix(orchestrator): UserMemoryTool never resolved its group scope
ginccc Aug 3, 2026
7c0b36e
fix(orchestrator): three SPI hardening fixes from PR review
ginccc Aug 3, 2026
8e2fc2c
feat(groups): Convergence detection + early exit (I2)
ginccc Aug 3, 2026
df2aadf
fix(orchestrator): restore interrupts wrapped in a cause chain
ginccc Aug 3, 2026
cc0fad3
feat(groups): Abstention + minority report (I4)
ginccc Aug 3, 2026
453217b
feat(groups): Structured verdicts + deterministic synthesis (I3)
ginccc Aug 3, 2026
66c7850
fix(groups): wouldExceedCeiling disagreed with enforceCeiling at zero
ginccc Aug 3, 2026
1c00ea8
feat(groups): Agent-writable shared task list (I5)
ginccc Aug 3, 2026
86307be
fix(groups): @Vetoed on GroupTaskTools — it broke application startup
ginccc Aug 3, 2026
03d5fbc
feat(groups): Runtime recruitment + configurable delegation timeout (I7)
ginccc Aug 3, 2026
31d3636
fix(groups): branch-review findings — cross-discussion write, ungated…
ginccc Aug 4, 2026
985c9d2
fix(groups): branch review round 2 — CME on persist, registry leak, c…
ginccc Aug 4, 2026
f7fee59
fix(groups): branch review round 3 — recruits were second-class every…
ginccc Aug 4, 2026
aca5a4a
fix(groups): a continuation round could report the previous round's c…
ginccc Aug 4, 2026
a84647e
test(orchestrator): cover two gates a real regression would have ship…
ginccc Aug 4, 2026
2a3dc6f
fix: the last five branch-review findings
ginccc Aug 4, 2026
8d5235a
fix(groups): pre-merge review — recruitment was inert, three earlier …
ginccc Aug 4, 2026
8306f6d
fix(orchestrator): MCP tool-name collision could run a different serv…
ginccc Aug 4, 2026
e00a940
Merge remote-tracking branch 'origin/main' into refactor/group-servic…
ginccc Aug 4, 2026
344d897
docs(plan): record implementation status and the gaps in what shipped
ginccc Aug 4, 2026
ed156c9
docs(plan): single handoff file so a new session knows what to build …
ginccc Aug 4, 2026
9ba135b
docs(plan): pin the verified anchors for the cost-pricing fix
ginccc Aug 4, 2026
326f0a9
docs(plan): file the round-5 review findings as pre-feature item N3
ginccc Aug 5, 2026
be86f17
Merge remote-tracking branch 'origin/main' into refactor/group-servic…
ginccc Aug 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,080 changes: 1,080 additions & 0 deletions docs/changelog.md

Large diffs are not rendered by default.

104 changes: 104 additions & 0 deletions docs/group-conversations.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,86 @@ create_group(
)
```

### Debate verdicts

When a DEBATE has **two sides and an impartial judge** — i.e. members carry at
least two distinct roles *and* `moderatorAgentId` names an agent that is not one
of the debaters — the judgment phase returns a structured verdict alongside its
prose, on the conversation's `decision` field:

```json
"decision": {
"type": "VERDICT",
"winner": "CON",
"tally": { "PRO": 4.0, "CON": 9.0 },
"outcome": "CON wins (PRO 4/10, CON 9/10) — PRO asserted; CON cited.",
"method": "debate-judgment",
"decidedAtPhase": "Judgment",
"dissents": []
}
```

`winner` is `null` for a tie (the tie is stated in `outcome`). The judge is asked
to score argument quality and factual support, and explicitly *not* assertiveness
or fluency.

Any of these leaves `decision` unset and the conclusion as ordinary prose — none
of them is an error:

- **No roles on the members.** Nothing argued PRO, so a PRO-vs-CON score would be
invented rather than measured.
- **No moderator.** The stand-in synthesizer is one of the debaters, and a
partisan's call is not the group's finding.
- **A phase `inputTemplate` of your own.** Your instruction wins; set one if you
want a debate to conclude in plain prose.
- **A judgment the parser cannot read.** The prose conclusion is kept as-is.

## Agent-filed tasks

Work an agent *discovers* mid-discussion — a missing migration, an untested
edge case — would otherwise die in prose: the shared task list is written only
by the PLAN phase and by config. Turning on `taskListConfig` gives members two
tools, `addGroupTask` and `listGroupTasks`, and a filed task is picked up by the
next execution wave with no other changes.

```json
"taskListConfig": {
"allowAgentTaskCreation": true,
"maxAgentAddedTasksPerDiscussion": 20,
"maxPerTurn": 3
}
```

**Off by default, and absent rather than refusing when off.** A tool that is not
assembled costs no prompt tokens and cannot be argued with; one that exists and
always says no invites retries. The tools appear only when the turn belongs to a
live group discussion whose config sets `allowAgentTaskCreation: true` — a
standalone agent, a paused discussion, or an unreadable config all yield no
tools.

`addGroupTask(subject, description, dependsOnSubjects?, priority?, assignToRole?)`

- **`dependsOnSubjects`** names other tasks by their *subject*, as
`listGroupTasks` prints them. An unknown name is refused, not dropped —
silently filing a task without its dependency schedules it immediately, which
is the opposite of what was asked.
- **`assignToRole`** takes `"ROLE:Reviewer"` or a member's exact name, and files
the task already assigned. Omitting it (or passing `"ALL"`) leaves the task
for the wave loop to assign, as it does any other. An unmatched role is
refused with the available roles named.
- Refusals are sentences aimed at the model: duplicate subject, unknown
dependency, circular dependency, subject over 200 chars, description over
4,000, and either cap being reached.

**No claim or complete tools.** The wave loop owns every task-state transition;
a second writer racing it would corrupt the state machine that decides what runs
next. Filing is the only agent-side write.

Both caps are enforced independently: `maxPerTurn` bounds a runaway single turn,
`maxAgentAddedTasksPerDiscussion` bounds slow drift across a long discussion. The
discussion cap counts only agent-filed tasks, so a large planned backlog does not
exhaust it. A rejected call does not consume the per-turn budget.

## Nested Groups (Group-of-Groups)

Members can be other groups. The sub-group runs its own discussion and its synthesized answer becomes the member's response.
Expand Down Expand Up @@ -233,8 +313,31 @@ During TASK_FORCE (or any group) discussions, agents with the appropriate LLM to
| `CreateSubAgentTool` | Create a new ephemeral agent with a specific system prompt |
| `ConverseWithAgentTool` | Delegate a sub-task to an existing deployed agent |
| `FindAgentsByCapabilityTool` | Discover agents by capability keywords |
| `RecruitAgentTool` | Bring a discovered agent into the discussion as a member |
| `TeardownAgentTool` | Clean up dynamically created agents |

#### Recruitment

Discovery and recruitment are two halves of one capability, and both are gated by
`allowRecruitment`. `findAgentsByCapability` locates a specialist;
`recruitAgent(agentId, role, reason)` brings it in.

A recruit **joins from the next round**, never mid-round — a roster that changed
while a round was running would move the speaker index a paused discussion
resumes from, and change the denominator the convergence and unanimity checks
already computed for the round in flight. The recruitment is recorded as a
`FACILITATION` transcript entry naming the recruiter, the recruit, the role and
the reason, so the rest of the team can see why the roster changed.

Recruitment is refused, with an actionable message, when the agent is not
deployed, is already a member, is the recruiter itself, or when
`maxRecruitedAgentsPerDiscussion` is reached.

**Recruits are never torn down.** They are pre-existing deployed agents the
discussion borrowed, so `TeardownAgentTool` and end-of-discussion cleanup leave
them alone — undeploying one would take it away from every other conversation
using it. Only agents the discussion *created* are cleaned up.

#### DynamicAgentConfig

Guardrails for dynamic agent creation are configured per-group via `AgentGroupConfiguration.dynamicAgents`:
Expand Down Expand Up @@ -268,6 +371,7 @@ Guardrails for dynamic agent creation are configured per-group via `AgentGroupCo
| `allowDelegation` | `true` | Allow delegating sub-tasks to other agents |
| `maxCreatedAgentsPerDiscussion` | `5` | Cap on new agents created per discussion |
| `maxRecruitedAgentsPerDiscussion` | `10` | Cap on recruited agents per discussion |
| `delegationTimeoutSeconds` | `60` | How long a delegating agent waits for its delegate's turn. Non-positive falls back to the default |
| `maxDelegationsPerTask` | `3` | Cap on delegations per task |
| `lifecyclePolicy` | `EPHEMERAL` | `EPHEMERAL`, `KEEP_DEPLOYED`, `UNDEPLOY_ONLY`, or `AGENT_DECIDES` |
| `inheritParentModel` | `true` | Created agents inherit the parent agent's model |
Expand Down
Loading
Loading