Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 15 additions & 9 deletions packages/app-bundle/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"packages/app/src/design-polish.css": "6b860316d2d57a4a9a89c1ff2772de45eca0d0cef2bd3e2ea8572d817e1d1910",
"packages/app/src/desktop-menu.ts": "350e0e9f2e4294bfe1b1f357b3cd2ea0908b14d20ca1e79fda2305db1958fb24",
"packages/app/src/entry.tsx": "f35e1017f4c9d478d254b2a38043e5750064b6bef25169c3e07ae9f72ff1049c",
"packages/app/src/index.css": "2c11df3dcebb381358e06626094b5dda3c35688ad7940d643b2cbb6cff1ea9e9",
"packages/app/src/index.css": "727656c5e7ff7d7c7ff4fc95a3ec7c9148a34f1f974a58a297285e870519be5d",
"packages/app/src/theme-preload.test.ts": "d9e4e96dd39a3491493637682611bedf6ddf4b6e4dbdddcb60bf006211b137a1",
"packages/app/test-browser/empty-workspace-landing.test.ts": "83d19ecf5da37c234dbde1a004432865204d7f412a335c4e470a07e73d9bcda4",
"packages/app/test-browser/keybind-migration.test.ts": "192418880d432ff62d15fd80f6250ece6233e43d0dbe11d5a4fbb5f4d9e45a06",
Expand Down Expand Up @@ -98,6 +98,7 @@
"packages/app/src/components/amicode-default-model.tsx": "16a2de1feb12e07416fe5fbb25879391f912ee0909b553c868c1b531f2ed1f2d",
"packages/app/src/components/amicode-defaults-capsule.tsx": "94dc081bc47abb7e50566fcd8e9e53124f9d7632d61f93eff4f1627e0c2efb2d",
"packages/app/src/components/amicode-file-ref-bridge.tsx": "11c17d7fdb29d16530e188b13ee8af728f37d9adfbb518084106ce50cc0c527a",
"packages/app/src/components/concise-mode-toggle.tsx": "3eff0c5557143500a27d828a08d1efe09a8c99aac9b6e2f9950bfe70c76162a1",
"packages/app/src/components/connection-banner.tsx": "c793966e1f9d6f307af697032b235c2ff65afaa04f6cc232e40ce45e5745ded4",
"packages/app/src/components/dialog-command-palette-v2.css": "85984957adaf15efcc0828310f3a0b438cedaaba776a9fec2eddb3ad83487fa2",
"packages/app/src/components/dialog-command-palette-v2.tsx": "e12da3e5e9e503089dd9139b20e04f6b566cf74fa445f23a2e193f5883f7c5f3",
Expand All @@ -115,7 +116,7 @@
"packages/app/src/components/pane-bridge.tsx": "756b3306b51c963fac6778716f6c6c4626f0f3bf75c93b7cb4cab58ea0bf3d4e",
"packages/app/src/components/profile-popover.tsx": "eff742e90544455f13234a9351bb1b82189663cba2cd3172bdcea1a8a37284e8",
"packages/app/src/components/prompt-input-clipboard-structure.test.ts": "946b718c2164da394602f26e4d9919d752da0d0b9bee714f345ba16c274542da",
"packages/app/src/components/prompt-input-v2.tsx": "5ab46e41bda4fa11e99f06c37df76c1fd3cce061c6ba7a9f85bee369a965b98a",
"packages/app/src/components/prompt-input-v2.tsx": "4a51a7823755514497497def61fd4b890f29e08573d7d727b303d31318f3b265",
"packages/app/src/components/prompt-input.tsx": "97a9bc55b46cf1f2d7c3e38110e736f7fa9db7f89121d3c013b9650a60d382d4",
"packages/app/src/components/prompt-project-selector.tsx": "0157c954b938bce2b5aca9a82f4a72338e361f4c9027e55a7fd5a14a1654d294",
"packages/app/src/components/report-bug-button.css": "909809b6241d53c386aa4c8ea34539ba162a8998fff755834c783025bc53a5fd",
Expand Down Expand Up @@ -242,7 +243,7 @@
"packages/app/src/utils/retry-import.ts": "591bfd3e28f8afa9c4d9f0ca118d20e875cb1dce574c0ae25615994c4fe57a99",
"packages/app/src/utils/serialize-session.test.ts": "f8fe318f195dca251eae3f4899f4a5881c3523522f59e02fef208588391f4048",
"packages/app/src/utils/serialize-session.ts": "c303c678500cdb64c7aa6928a0bd984c441a36705fe8152368a0e575cbcb459f",
"packages/app/src/utils/server-compat.ts": "01413e13b87485770eb3dbec0b882aec4c8247dafc4dc74b7b4669fa5b20cf1a",
"packages/app/src/utils/server-compat.ts": "1cccb1aabde1bce6ee382c33db0dea9273a9519747b805b771dec060d3071b39",
"packages/app/src/utils/session-message.ts": "96f8f3dca62f70215ac2dccbc8d27cd5b247af3ecea1ac5d34a74ffc8af5d4d3",
"packages/app/src/utils/start-prompt.ts": "809d8ad0ab6de76a97503f8368ade25f771431be3f92eb8f039a2c65244ab20f",
"packages/app/src/utils/vscode-explorer-icon-theme.test.ts": "351deb0ff77e62d2585858105aaf34ab621298511798fcb626abee97bd644fe6",
Expand Down Expand Up @@ -622,7 +623,7 @@
"packages/app/src/components/prompt-input/placeholder.ts": "6b2af23a0588e7970cd0dd059fc60c3671c907a76ba3db660483a932f1dce79d",
"packages/app/src/components/prompt-input/slash-popover.tsx": "a1800173edebcd419802796470f7270acaf0ef689a72f1b81a1cedfd8968337d",
"packages/app/src/components/prompt-input/submit-stream-gap.test.ts": "68a94b03e75625531b2f2ad161ef9d0b93365f9edae154a797f82031b5bfc062",
"packages/app/src/components/prompt-input/submit.ts": "6f14368b0d481bdf10a2711e08da41010edd9f67ca8af90b930c096a56778b89",
"packages/app/src/components/prompt-input/submit.ts": "ea6818c3d0570f382b355b31c23ce3c654d726b277c3413dcc2d343c2f4ea555",
"packages/app/src/components/session/context-warning-banner.tsx": "f5fab4d7a9536238b70d64488fc30d44f6a4948fdcffd910a9666f4b6c296019",
"packages/app/src/components/session/index.ts": "21473290d4a1a3d0670fd878372ddf21ff22d4a0b30d1aef6c81ea906c1488b4",
"packages/app/src/components/session/panel-menu.tsx": "42f323046b7375ae158023a51506038eb15f2e1545c407f45acd47446a8ef3e0",
Expand Down Expand Up @@ -858,7 +859,9 @@
"packages/opencode/src/server/routes/instance/httpapi/handlers/file.ts": "10bcb79b117beebee04184fb48c1ad789e782e2f7ae4b0c68603edc00f03aee9",
"packages/opencode/src/server/routes/instance/httpapi/handlers/mcp.ts": "7da08d58765cdf2b6ce4648fc70de371402949f5bb7b75696f674d43f2d5365b",
"packages/opencode/src/server/routes/instance/httpapi/handlers/provider.ts": "8baf16cad0992c5351b61b0c0fb27903bb25b17e1b25fc7c5d77f807c24413a6",
"packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts": "b37ff4136c4aea85b23083bf441f03e09820e3e4cdd95e4118ca570d8435b94f"
"packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts": "b37ff4136c4aea85b23083bf441f03e09820e3e4cdd95e4118ca570d8435b94f",
"packages/app/src/context/concise-mode.ts": "b03ac887222543e590d31781c944e69aca97bff32a661751dd817d46bf4ead64",
"packages/app/src/context/concise-mode.test.ts": "534258a1d82d66bd8b482bb3a8ca6a24d508328505abd0f7f52e1b75cdf57fa2"
},
"symlinks": [
"packages/app/public/amico.svg"
Expand All @@ -873,10 +876,10 @@
"packages/ui/src/v2/components/text-shimmer-v2.tsx"
],
"exceptions": [],
"extracted_at": "2026-09-24T04:46:38.771Z",
"extracted_at": "2026-09-30T02:26:16.851Z",
"per_package": {
"packages/app": {
"A": 186,
"A": 189,
"M": 157,
"D": 0
},
Expand Down Expand Up @@ -922,7 +925,7 @@
}
},
"counts": {
"overlay_total": 851,
"overlay_total": 854,
"deletions": 7,
"server_coupled": 0
},
Expand Down Expand Up @@ -1017,6 +1020,7 @@
"packages/app/src/components/amicode-default-model.tsx": "A",
"packages/app/src/components/amicode-defaults-capsule.tsx": "A",
"packages/app/src/components/amicode-file-ref-bridge.tsx": "A",
"packages/app/src/components/concise-mode-toggle.tsx": "A",
"packages/app/src/components/connection-banner.tsx": "A",
"packages/app/src/components/dialog-command-palette-v2.css": "M",
"packages/app/src/components/dialog-command-palette-v2.tsx": "M",
Expand Down Expand Up @@ -1777,7 +1781,9 @@
"packages/opencode/src/server/routes/instance/httpapi/handlers/file.ts": "M",
"packages/opencode/src/server/routes/instance/httpapi/handlers/mcp.ts": "M",
"packages/opencode/src/server/routes/instance/httpapi/handlers/provider.ts": "M",
"packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts": "M"
"packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts": "M",
"packages/app/src/context/concise-mode.ts": "A",
"packages/app/src/context/concise-mode.test.ts": "A"
},
"upstream_base_archive_sha256": "4b5f2e5edfc796082a353fd09c1b1dec3e7a108a4b080fe3a86b12d9fa59fae6"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// amicode: concise-mode toggle for the v2 composer's bottom control row.
// A real switch (not an icon) — flipping it does NOT send a message; it sets a
// persistent per-session flag that, while on, attaches a concise directive to
// the NEXT message you send (as the invisible per-turn system instruction), so
// the response comes back concise. Turning it off stops that. The switch's own
// accent/checked styling (switch-v2.css) carries the on state; the "Concise"
// label is self-explanatory, so there is no tooltip.
import { Switch } from "@opencode-ai/ui/v2/switch-v2"

export function ConciseModeToggle(props: { active: boolean; disabled?: boolean; onToggle: (next: boolean) => void }) {
return (
<Switch
data-action="concise-mode"
checked={props.active}
disabled={props.disabled}
onChange={(next) => props.onToggle(next)}
>
Concise
</Switch>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import { KeybindV2 } from "@opencode-ai/ui/v2/keybind-v2"
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
import type { ReferenceInfo } from "@opencode-ai/sdk/v2/client"
import { createEffect, createMemo, on, onCleanup, Show } from "solid-js"
import { useSearchParams } from "@solidjs/router"
import { bindConcise, conciseKey, conciseMode } from "@/context/concise-mode"
import { ModelSelectorPopoverV2 } from "@/components/dialog-select-model"
import { DialogSelectModelUnpaidV2 } from "@/components/dialog-select-model-unpaid-v2"
import { ReportBugButton } from "@/components/report-bug-button"
import { ConciseModeToggle } from "@/components/concise-mode-toggle"
import type { PromptInputProps } from "@/components/prompt-input/contracts"
import { normalizePromptHistoryEntry, promptLength, type PromptHistoryComment } from "@/components/prompt-input/history"
import { createPersistedPromptInputHistory } from "@/components/prompt-input/history-store"
Expand Down Expand Up @@ -48,6 +51,10 @@ export type PromptInputV2ComposerProps = {
export type PromptInputV2ControllerProps = Omit<PromptInputProps, "class" | "submission">
export type PromptInputV2ComposerController = PromptInputV2Interaction & {
readonly model: PromptInputProps["controls"]["model"]
// amicode: the concise-mode toggle state + setter, surfaced to the composer's
// trailing controls. `on` is a reactive accessor; `set` flips a persistent
// per-session flag — it sends nothing. The flag is read at submit time.
readonly concise: { readonly on: () => boolean; readonly set: (next: boolean) => void }
}

export function PromptInputV2Composer(props: PromptInputV2ComposerProps) {
Expand All @@ -56,7 +63,7 @@ export function PromptInputV2Composer(props: PromptInputV2ComposerProps) {
const language = useLanguage()

return (
<div class="flex flex-col gap-3">
<div class="amicode-composer-cq flex flex-col gap-3">
<PromptInputV2
controller={props.controller}
borderUnderlay={props.borderUnderlay}
Expand All @@ -67,7 +74,17 @@ export function PromptInputV2Composer(props: PromptInputV2ComposerProps) {
// amicode/opencode#116: report-a-bug, right-anchored immediately left
// of send. The boot-param gate lives HERE (not inside the button) so a
// gated-off button passes `undefined` and the row's layout never shifts.
trailingControl={bugReportEnabled() ? <ReportBugButton /> : undefined}
trailingControl={
<>
<div class="amicode-concise-toggle-wrap flex items-center">
<ConciseModeToggle
active={props.controller.concise.on()}
onToggle={(next) => props.controller.concise.set(next)}
/>
</div>
{bugReportEnabled() ? <ReportBugButton /> : undefined}
</>
}
modelControl={
<PromptInputV2ModelControl
loading={props.controller.model.loading}
Expand Down Expand Up @@ -216,6 +233,21 @@ export function usePromptInputV2Controller(props: PromptInputV2ControllerProps):
if (!id) return permission.isAutoAcceptingDirectory(sdk().directory)
return permission.isAutoAccepting(id, sdk().directory)
})
// amicode: concise-mode flag. A per-session switch — flipping it sends
// nothing; it is read at submit time (createPromptSubmit's `concise`) to attach
// the concise directive to the outgoing prose message. #1651: held in the
// session-keyed concise store, NOT component state, so a composer remount (new
// chat → session, Changes tab, session switch) reads the value back. A new
// chat writes under its draft key; submit promotes it to the session key.
const [conciseSearch] = useSearchParams<{ draftId?: string }>()
const conciseKeyNow = () => {
const id = props.controls.session.id
return id
? conciseKey.session(sdk().scope, sdk().directory, id)
: conciseKey.draft(sdk().scope, sdk().directory, conciseSearch.draftId)
}
const concise = bindConcise(conciseMode, conciseKeyNow)
const conciseOn = concise.on
const submission = createPromptSubmit({
prompt,
info,
Expand All @@ -241,6 +273,7 @@ export function usePromptInputV2Controller(props: PromptInputV2ControllerProps):
onSubmit: props.onSubmit,
streamGap,
model: props.controls.model.selection,
concise: conciseOn,
})

const referenceDescription = (reference: ReferenceInfo) =>
Expand Down Expand Up @@ -437,6 +470,7 @@ export function usePromptInputV2Controller(props: PromptInputV2ControllerProps):
},
})
Object.defineProperty(controller, "model", { get: () => props.controls.model })
Object.defineProperty(controller, "concise", { get: () => concise })

// Framed webview: the window-level fallback (global-clipboard.ts) is the
// sole ⌘V owner. When the clipboard carries no text it offers the media to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { buildRequestParts } from "./build-request-parts"
import { setCursorPosition } from "./editor-dom"
import { formatServerError } from "@/utils/server-errors"
import { ScopedKey } from "@/utils/server-scope"
import { conciseKey, conciseMode } from "@/context/concise-mode"
import { createPromptSubmissionState } from "./submission-state"
import { normalizeSessionInfo } from "@/utils/session"
import { Event } from "@opencode-ai/schema/event"
Expand All @@ -40,6 +41,9 @@ export type FollowupDraft = {
agent: string
model: { providerID: string; modelID: string }
variant?: string
// amicode: per-turn system instruction (e.g. the concise directive). Reaches
// the model for this turn but is NOT rendered as a visible message part.
system?: string
}

type FollowupSendInput = {
Expand All @@ -56,6 +60,12 @@ const draftText = (prompt: Prompt) => prompt.map((part) => ("content" in part ?

const draftImages = (prompt: Prompt) => prompt.filter((part): part is ImageAttachmentPart => part.type === "image")

// amicode: the concise directive. When the composer's concise switch is on it
// rides as the message's per-turn `system` instruction (invisible — reaches the
// model, not rendered as a user part), never appended to the visible text.
const CONCISE_DIRECTIVE =
"Concise mode is on for this message. Respond concisely: lead with the answer or next action, no preamble or closing pleasantries, and keep it tight — without dropping required caveats, questions, or safety confirmations."

export async function sendFollowupDraft(input: FollowupSendInput) {
const text = draftText(input.draft.prompt)
const images = draftImages(input.draft.prompt)
Expand Down Expand Up @@ -135,6 +145,7 @@ export async function sendFollowupDraft(input: FollowupSendInput) {
time: { created: Date.now() },
agent: input.draft.agent,
model: { ...input.draft.model, variant: input.draft.variant },
system: input.draft.system,
}

const add = () =>
Expand Down Expand Up @@ -172,6 +183,7 @@ export async function sendFollowupDraft(input: FollowupSendInput) {
agent: input.draft.agent,
model: input.draft.model,
variant: input.draft.variant,
system: input.draft.system,
legacyParts: requestParts,
text: requestParts.flatMap((part) => (part.type === "text" ? [part.text] : [])).join("\n"),
files: requestParts.flatMap((part) => {
Expand Down Expand Up @@ -234,6 +246,12 @@ type PromptSubmitInput = {
* notice instead of optimistically posting into a dead tunnel. */
streamGap?: Accessor<boolean>
model?: ModelSelection
/** amicode: when true at send time, the concise directive rides as the
* message's per-turn `system` instruction (normal prose turns only) so the
* response comes back concise — the composer's concise switch drives this.
* #1651: the switch is session-keyed; a new chat's value is promoted to the
* created session below. */
concise?: Accessor<boolean>
}

export function createPromptSubmit(input: PromptSubmitInput) {
Expand Down Expand Up @@ -372,6 +390,10 @@ export function createPromptSubmit(input: PromptSubmitInput) {
const permissionState = permission.currentServerState()
const isNewSession = !params.id
const shouldAutoAccept = isNewSession && input.autoAccept()
// amicode#1651: read the concise switch (and the new chat's draft key) NOW,
// before any await — once the session exists the composer's key moves to it.
const conciseAtSend = input.concise?.() ?? false
const conciseDraftKey = conciseKey.draft(sdk().scope, projectDirectory, search.draftId)
const worktreeSelection = input.newSessionWorktree?.() || "main"

let sessionDirectory = projectDirectory
Expand Down Expand Up @@ -444,6 +466,8 @@ export function createPromptSubmit(input: PromptSubmitInput) {
model: { providerID: currentModel.provider.id, modelID: currentModel.id },
variant: variant ?? null,
})
// amicode#1651: the new chat's concise switch follows it into the session.
conciseMode.promote(conciseDraftKey, conciseKey.session(sdk().scope, sessionDirectory, session.id))
layout.handoff.setTabs(base64Encode(sessionDirectory), session.id)
const draftID = search.draftId
if (draftID) tabs.promoteDraft(draftID, { server: tabs.draft(draftID).server, sessionId: session.id })
Expand Down Expand Up @@ -473,6 +497,7 @@ export function createPromptSubmit(input: PromptSubmitInput) {
agent,
model,
variant,
system: conciseAtSend ? CONCISE_DIRECTIVE : undefined,
}

const clearInput = () => {
Expand Down
Loading
Loading