@asm-agent/coding-agent 0.9.3 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/bundled-extensions/00-COMECE-AQUI.txt +233 -0
- package/bundled-extensions/00-INDEX.md +267 -0
- package/bundled-extensions/OLLAMA-TOOL-FORCING-IMPLEMENTATION.md +323 -0
- package/bundled-extensions/OLLAMA-TOOL-FORCING-QUICK-START.txt +113 -0
- package/bundled-extensions/OLLAMA-TOOL-FORCING-TESTS.md +217 -0
- package/bundled-extensions/OLLAMA-TOOL-FORCING.md +155 -0
- package/bundled-extensions/README.txt +178 -0
- package/bundled-extensions/ollama-tool-forcing.ts +90 -0
- package/dist/asm-agent-runtime/run_test.py +19 -0
- package/dist/asm-agent-runtime/src/rlm/__pycache__/__init__.cpython-312.pyc +0 -0
- package/dist/asm-agent-runtime/src/rlm/__pycache__/bash.cpython-312.pyc +0 -0
- package/dist/asm-agent-runtime/src/rlm/__pycache__/repl.cpython-312.pyc +0 -0
- package/dist/asm-agent-runtime/test/__pycache__/test_bash.cpython-312.pyc +0 -0
- package/dist/asm-agent-runtime/test/test_bash.py +5 -3
- package/dist/bundle/{bundled-modules-ZKJOKVMT.js → bundled-modules-ABB56VC3.js} +2 -2
- package/dist/bundle/{chunk-IP6ZYRWR.js → chunk-GUPU2EEK.js} +13 -58
- package/dist/bundle/{chunk-YWSYGTH7.js → chunk-NESKY6T7.js} +2 -2
- package/dist/bundle/{cli-main-YZ2VGIP6.js → cli-main-ZYCGVTZE.js} +2 -2
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/{main-JTKNY7OB.js → main-J36EPND6.js} +2 -2
- package/dist/core/auth-storage.d.ts +0 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +0 -49
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/modes/interactive/auth-flows.d.ts.map +1 -1
- package/dist/modes/interactive/auth-flows.js +1 -1
- package/dist/modes/interactive/auth-flows.js.map +1 -1
- package/dist/postinstall.d.ts.map +1 -1
- package/dist/postinstall.js +40 -3
- package/dist/postinstall.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +7 -6
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Ollama Tool-Forcing Extension
|
|
2
|
+
|
|
3
|
+
Esta extensão resolve um problema comum ao usar modelos abertos menores (Qwen 27b, Llama, etc) rodando via Ollama com o ASM Agent.
|
|
4
|
+
|
|
5
|
+
## Problema
|
|
6
|
+
|
|
7
|
+
Quando você roda um modelo menor localmente via Ollama, o seguinte comportamento pode ocorrer:
|
|
8
|
+
|
|
9
|
+
1. O modelo começa a **gerar seu raciocínio** (thinking/internal reasoning)
|
|
10
|
+
2. **Após pensar**, o modelo **não chama nenhuma ferramenta**
|
|
11
|
+
3. O modelo **não gera nenhum texto final** para o usuário
|
|
12
|
+
4. O modelo simplesmente **emite um "token de parada"** e encerra
|
|
13
|
+
5. O ASM Agent fica **parado esperando você falar**
|
|
14
|
+
|
|
15
|
+
Isso não é um "bug" — é um comportamento comum de modelos menores quando não há instrução explícita.
|
|
16
|
+
|
|
17
|
+
## Solução
|
|
18
|
+
|
|
19
|
+
Esta extensão **injeta automaticamente** uma instrução forte no system prompt que:
|
|
20
|
+
|
|
21
|
+
- ✅ **Força o modelo a chamar ferramentas** antes de parar
|
|
22
|
+
- ✅ **Proíbe gerações vazias** (só thinking sem ação)
|
|
23
|
+
- ✅ **Mantém o loop autônomo** funcionando continuamente
|
|
24
|
+
- ✅ **Funciona automaticamente** — sem precisar de comandos especiais
|
|
25
|
+
|
|
26
|
+
## Como Usar
|
|
27
|
+
|
|
28
|
+
### 1. Ativar a Extensão
|
|
29
|
+
|
|
30
|
+
A extensão já foi colocada em:
|
|
31
|
+
```
|
|
32
|
+
~/.asm-agent/extensions/ollama-tool-forcing.ts
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Ela é **auto-descoberta** — não precisa fazer nada especial. Será carregada automaticamente na próxima vez que você iniciar o ASM Agent.
|
|
36
|
+
|
|
37
|
+
### 2. Recarregar em Tempo Real
|
|
38
|
+
|
|
39
|
+
Se o ASM Agent já está rodando, use o comando:
|
|
40
|
+
```
|
|
41
|
+
/reload
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Isso recarrega todas as extensões sem precisar reiniciar.
|
|
45
|
+
|
|
46
|
+
### 3. Verificar Status
|
|
47
|
+
|
|
48
|
+
Use o comando:
|
|
49
|
+
```
|
|
50
|
+
/ollama-tools-info
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Ele mostra se a extensão está ativa (✅) e permite fazer toggle (ligar/desligar).
|
|
54
|
+
|
|
55
|
+
## O Que a Extensão Faz
|
|
56
|
+
|
|
57
|
+
Quando você envia uma tarefa, a extensão adiciona isto ao final do system prompt:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
## ⚠️ REGRA CRÍTICA: TOOL-FORCING PARA MODELOS LOCAIS
|
|
61
|
+
|
|
62
|
+
Você está rodando num modelo aberto (Ollama/local). Para garantir que trabalhe autonomamente:
|
|
63
|
+
|
|
64
|
+
1. **NUNCA pare sem chamar uma ferramenta**: Depois de pensar/raciocinar, você DEVE chamar
|
|
65
|
+
uma ferramenta (bash, edit, ipython, etc) para continuar o trabalho.
|
|
66
|
+
|
|
67
|
+
2. **Sem gerações vazias**: Não é permitido apenas gerar um "thinking" e depois parar.
|
|
68
|
+
|
|
69
|
+
3. **Ferramentas = Continuidade**: Use-as para:
|
|
70
|
+
- Verificar status (bash, ipython)
|
|
71
|
+
- Fazer alterações (edit, bash)
|
|
72
|
+
- Validar resultados (bash com testes)
|
|
73
|
+
- Avançar a tarefa passo a passo
|
|
74
|
+
|
|
75
|
+
4. **Padrão de turno correto**:
|
|
76
|
+
a) Receber tarefa e pensar (thinking)
|
|
77
|
+
b) Chamar uma ferramenta para agir
|
|
78
|
+
c) Observar resultado
|
|
79
|
+
d) Voltar ao passo (a) se precisar avançar
|
|
80
|
+
|
|
81
|
+
5. **Proibido**: Terminar turno com apenas uma seção vazia de thinking.
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Quando Usar
|
|
85
|
+
|
|
86
|
+
- ✅ Rodando **Qwen 27b/32b** via Ollama
|
|
87
|
+
- ✅ Rodando **Llama** via Ollama
|
|
88
|
+
- ✅ Rodando qualquer **modelo aberto local**
|
|
89
|
+
- ✅ Modelo que às vezes **congela ou gera vazio**
|
|
90
|
+
|
|
91
|
+
## Quando Pode Desativar
|
|
92
|
+
|
|
93
|
+
- Se rodando **Claude/GPT** (APIs online)
|
|
94
|
+
- Se o modelo está **funcionando bem** sem a regra
|
|
95
|
+
- Quando quer testar comportamento natural
|
|
96
|
+
|
|
97
|
+
Use `/ollama-tools-info` para toggle sem reiniciar.
|
|
98
|
+
|
|
99
|
+
## Dicas Adicionais
|
|
100
|
+
|
|
101
|
+
### Para melhor desempenho com Qwen:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Use a versão Coder (superior para tool-calling)
|
|
105
|
+
ollama pull qwen2.5-coder:32b
|
|
106
|
+
|
|
107
|
+
# Rode com mais contexto
|
|
108
|
+
ollama run qwen2.5-coder:32b
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Atualize o Ollama:
|
|
112
|
+
|
|
113
|
+
Versões antigas do Ollama podem engolir chamadas de ferramenta do Qwen. Mantenha atualizado:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
ollama --version
|
|
117
|
+
# Se antigo, atualize
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Use a extensão + instruções diretas:
|
|
121
|
+
|
|
122
|
+
Se o modelo ainda ficar preso, você pode também dar um empurrãozinho manualmente:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
"Como está o andamento? Use a ferramenta de bash para verificar e continue o trabalho imediatamente."
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Removendo a Extensão
|
|
129
|
+
|
|
130
|
+
Se quiser remover a regra permanentemente:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
rm ~/.asm-agent/extensions/ollama-tool-forcing.ts
|
|
134
|
+
/reload
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Arquivos Relacionados
|
|
138
|
+
|
|
139
|
+
- **Extensão**: `~/.asm-agent/extensions/ollama-tool-forcing.ts`
|
|
140
|
+
- **Documentação**: Este arquivo
|
|
141
|
+
- **Comando**: `/ollama-tools-info` (dentro do ASM Agent)
|
|
142
|
+
- **Reload**: `/reload` (para recarregar extensões)
|
|
143
|
+
|
|
144
|
+
## Suporte
|
|
145
|
+
|
|
146
|
+
Se a extensão não funcionar:
|
|
147
|
+
|
|
148
|
+
1. Verifique se o arquivo está em `~/.asm-agent/extensions/`
|
|
149
|
+
2. Use `/reload` para recarregar
|
|
150
|
+
3. Use `/ollama-tools-info` para verificar status
|
|
151
|
+
4. Verifique logs do ASM Agent para erros de compilação TypeScript
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
**Criado para resolver o problema de modelos abertos gerando vazios com ASM Agent.**
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
|
|
2
|
+
╔════════════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
║ 🎉 CUSTOM RULE CRIADA E PRONTA! ║
|
|
4
|
+
║ Ollama Tool-Forcing Extension v1.0 ║
|
|
5
|
+
╚════════════════════════════════════════════════════════════════════════════════╝
|
|
6
|
+
|
|
7
|
+
📂 LOCALIZAÇÃO:
|
|
8
|
+
~/.asm-agent/extensions/
|
|
9
|
+
|
|
10
|
+
📦 ARQUIVOS CRIADOS (5 arquivos, ~18 KB total):
|
|
11
|
+
|
|
12
|
+
1. ollama-tool-forcing.ts (3.2 KB)
|
|
13
|
+
→ Extensão principal (TypeScript)
|
|
14
|
+
→ Auto-descoberta pelo ASM Agent
|
|
15
|
+
→ Sem dependências externas
|
|
16
|
+
|
|
17
|
+
2. OLLAMA-TOOL-FORCING.md (4.4 KB)
|
|
18
|
+
→ Manual completo
|
|
19
|
+
→ Explicação detalhada do problema
|
|
20
|
+
→ Quando usar/não usar
|
|
21
|
+
→ Dicas de otimização
|
|
22
|
+
|
|
23
|
+
3. OLLAMA-TOOL-FORCING-QUICK-START.txt (5.8 KB)
|
|
24
|
+
→ Referência rápida visual
|
|
25
|
+
→ Instruções em poucas linhas
|
|
26
|
+
→ Próximos passos
|
|
27
|
+
|
|
28
|
+
4. OLLAMA-TOOL-FORCING-TESTS.md (4.5 KB)
|
|
29
|
+
→ 5 exemplos de teste
|
|
30
|
+
→ Como verificar se funciona
|
|
31
|
+
→ Troubleshooting
|
|
32
|
+
|
|
33
|
+
5. OLLAMA-TOOL-FORCING-IMPLEMENTATION.md (6.5 KB)
|
|
34
|
+
→ Guia de implementação
|
|
35
|
+
→ Como funciona internamente
|
|
36
|
+
→ Personalização avançada
|
|
37
|
+
|
|
38
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
39
|
+
|
|
40
|
+
✨ O QUE ESSA CUSTOM RULE FAZ:
|
|
41
|
+
|
|
42
|
+
┌─ PROBLEMA ORIGINAL ────────────────────────────────────────────────────────┐
|
|
43
|
+
│ │
|
|
44
|
+
│ Você roda Qwen/Llama via Ollama com ASM Agent: │
|
|
45
|
+
│ ❌ Modelo pensa um pouco │
|
|
46
|
+
│ ❌ Modelo para (emit stop token) │
|
|
47
|
+
│ ❌ Modelo não chama ferramenta │
|
|
48
|
+
│ ❌ ASM Agent fica esperando "e aí?" │
|
|
49
|
+
│ ❌ Loop autônomo quebrado │
|
|
50
|
+
│ │
|
|
51
|
+
└────────────────────────────────────────────────────────────────────────────┘
|
|
52
|
+
|
|
53
|
+
↓ SOLUÇÃO ↓
|
|
54
|
+
|
|
55
|
+
┌─ COM A EXTENSÃO ──────────────────────────────────────────────────────────┐
|
|
56
|
+
│ │
|
|
57
|
+
│ ✅ Extensão intercepta requisição (before_agent_start) │
|
|
58
|
+
│ ✅ Injeta regra CRÍTICA no system prompt │
|
|
59
|
+
│ ✅ Modelo recebe instrução forte: "NUNCA PARE SEM CHAMAR FERRAMENTA" │
|
|
60
|
+
│ ✅ Modelo é forçado a chamar bash/edit/ipython │
|
|
61
|
+
│ ✅ Loop autônomo continua funcionando │
|
|
62
|
+
│ ✅ Tarefa completada sem intervenção │
|
|
63
|
+
│ │
|
|
64
|
+
└────────────────────────────────────────────────────────────────────────────┘
|
|
65
|
+
|
|
66
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
67
|
+
|
|
68
|
+
🚀 USAR AGORA (3 PASSOS):
|
|
69
|
+
|
|
70
|
+
1️⃣ NADA! A extensão já está em ~/.asm-agent/extensions/
|
|
71
|
+
|
|
72
|
+
2️⃣ Próxima vez que iniciar ASM Agent:
|
|
73
|
+
- Extensão será auto-descoberta
|
|
74
|
+
- Regra será injetada automaticamente
|
|
75
|
+
- Funcionará em TODAS as requisições
|
|
76
|
+
|
|
77
|
+
3️⃣ Se ASM Agent já está rodando:
|
|
78
|
+
/reload (recarrega extensões)
|
|
79
|
+
/ollama-tools-info (verifica status)
|
|
80
|
+
|
|
81
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
82
|
+
|
|
83
|
+
📋 REGRA INJETADA NO SYSTEM PROMPT:
|
|
84
|
+
|
|
85
|
+
## ⚠️ REGRA CRÍTICA: TOOL-FORCING PARA MODELOS LOCAIS
|
|
86
|
+
|
|
87
|
+
1. NUNCA pare sem chamar uma ferramenta
|
|
88
|
+
2. Sem gerações vazias (só thinking não é permitido)
|
|
89
|
+
3. Ferramentas = continuidade (use bash, edit, ipython constantemente)
|
|
90
|
+
4. Padrão correto: pensar → agir → observar → repetir
|
|
91
|
+
5. Proibido: terminar turno sem chamar ferramenta
|
|
92
|
+
|
|
93
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
94
|
+
|
|
95
|
+
💻 TESTE AGORA:
|
|
96
|
+
|
|
97
|
+
Cole isso no ASM Agent com seu Qwen/Llama rodando:
|
|
98
|
+
|
|
99
|
+
"Crie um arquivo 'test.txt' com conteúdo 'Olá Ollama!' e use bash
|
|
100
|
+
para verificar que foi criado corretamente."
|
|
101
|
+
|
|
102
|
+
Resultado esperado:
|
|
103
|
+
✅ Modelo chama edit (criar arquivo)
|
|
104
|
+
✅ Modelo chama bash (verificar)
|
|
105
|
+
✅ Tarefa completa, sem ele ficar preso
|
|
106
|
+
|
|
107
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
108
|
+
|
|
109
|
+
📚 DOCUMENTAÇÃO:
|
|
110
|
+
|
|
111
|
+
Arquivo │ Objetivo │ Leia quando...
|
|
112
|
+
──────────────────────┼─────────────────────────────────┼──────────────────
|
|
113
|
+
OLLAMA-TOOL-FORCING.md │ Manual completo │ Quer detalhes
|
|
114
|
+
QUICK-START.txt │ Referência rápida │ Quer começar logo
|
|
115
|
+
TESTS.md │ 5 exemplos de teste │ Quer testar agora
|
|
116
|
+
IMPLEMENTATION.md │ Como funciona internamente │ Quer customizar
|
|
117
|
+
|
|
118
|
+
Todos em ~/.asm-agent/extensions/
|
|
119
|
+
|
|
120
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
121
|
+
|
|
122
|
+
⚙️ PERSONALIZAÇÃO:
|
|
123
|
+
|
|
124
|
+
Quer mudar o comportamento?
|
|
125
|
+
|
|
126
|
+
Edite: ~/.asm-agent/extensions/ollama-tool-forcing.ts
|
|
127
|
+
Depois: /reload
|
|
128
|
+
|
|
129
|
+
Quer desativar temporariamente?
|
|
130
|
+
|
|
131
|
+
/ollama-tools-info (faz toggle)
|
|
132
|
+
|
|
133
|
+
Quer remover permanentemente?
|
|
134
|
+
|
|
135
|
+
rm ~/.asm-agent/extensions/ollama-tool-forcing.ts
|
|
136
|
+
/reload
|
|
137
|
+
|
|
138
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
139
|
+
|
|
140
|
+
🎯 RESUMO TÉCNICO:
|
|
141
|
+
|
|
142
|
+
• TYPE: Custom Rule (Extensão ASM Agent)
|
|
143
|
+
• LINGUAGEM: TypeScript (compilado automaticamente)
|
|
144
|
+
• TAMANHO: ~3 KB executável, impacto ZERO em performance
|
|
145
|
+
• COMPATIBILIDADE: Qualquer modelo aberto (Qwen, Llama, Mistral, etc)
|
|
146
|
+
• DEPENDÊNCIAS: Nenhuma (usa API nativa do ASM Agent)
|
|
147
|
+
• ATIVAÇÃO: Auto-descoberta (~/.asm-agent/extensions/)
|
|
148
|
+
• SCOPO: Injeta regra no system prompt (antes do modelo pensar)
|
|
149
|
+
• REVERT: Pode ser desativado ou removido a qualquer hora
|
|
150
|
+
|
|
151
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
152
|
+
|
|
153
|
+
✅ CHECKLIST DE IMPLEMENTAÇÃO:
|
|
154
|
+
|
|
155
|
+
☑ Extensão criada: ~/.asm-agent/extensions/ollama-tool-forcing.ts
|
|
156
|
+
☑ Documentação escrita: 4 arquivos .md/.txt
|
|
157
|
+
☑ Comando registrado: /ollama-tools-info
|
|
158
|
+
☑ Auto-descoberta: Funcionará na próxima sessão
|
|
159
|
+
☑ Exemplos de teste: OLLAMA-TOOL-FORCING-TESTS.md
|
|
160
|
+
☑ Troubleshooting: Incluído em OLLAMA-TOOL-FORCING.md
|
|
161
|
+
☑ Sem dependencies: 100% nativo do ASM Agent
|
|
162
|
+
|
|
163
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
164
|
+
|
|
165
|
+
🎬 PRÓXIMA AÇÃO:
|
|
166
|
+
|
|
167
|
+
1. Reinicie ASM Agent (ou use /reload se já estiver rodando)
|
|
168
|
+
2. Teste com uma tarefa simples
|
|
169
|
+
3. Observe o modelo chamar ferramentas continuamente
|
|
170
|
+
4. Aproveite o loop autônomo sem interrupções!
|
|
171
|
+
|
|
172
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
173
|
+
|
|
174
|
+
╔════════════════════════════════════════════════════════════════════════════════╗
|
|
175
|
+
║ 🎊 Configuração completa! Bom trabalho! 🎊 ║
|
|
176
|
+
║ Seus modelos Ollama agora trabalham melhor ║
|
|
177
|
+
║ com o ASM Agent autonomamente. ║
|
|
178
|
+
╚════════════════════════════════════════════════════════════════════════════════╝
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ollama Tool-Forcing Extension
|
|
3
|
+
*
|
|
4
|
+
* Força modelos open-weights rodando via Ollama a sempre chamar uma ferramenta
|
|
5
|
+
* antes de parar, evitando que gerem apenas tokens de parada sem completar tarefas.
|
|
6
|
+
*
|
|
7
|
+
* Problema: Modelos menores (Qwen 27b, Llama, etc) às vezes apenas pensam
|
|
8
|
+
* e depois emitem um stop token sem chamar ferramentas ou gerar texto.
|
|
9
|
+
*
|
|
10
|
+
* Solução: Injeta no system prompt uma instrução forte que força o modelo a:
|
|
11
|
+
* 1. Nunca parar sem chamar uma ferramenta
|
|
12
|
+
* 2. Usar ferramentas para continuar o trabalho
|
|
13
|
+
* 3. Sempre completar o ciclo de tarefas
|
|
14
|
+
*
|
|
15
|
+
* Uso:
|
|
16
|
+
* 1. Copie este arquivo para ~/.asm-agent/extensions/ (global)
|
|
17
|
+
* ou .asm-agent/extensions/ (projeto-local)
|
|
18
|
+
* 2. Use /reload para ativar (se em modo interativo)
|
|
19
|
+
* 3. A extensão funciona automaticamente em todas as requisições
|
|
20
|
+
*
|
|
21
|
+
* Opcional: Use /ollama-tools-info para ver o status
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { ExtensionAPI } from "@asm-agent/coding-agent";
|
|
25
|
+
|
|
26
|
+
export default function ollamaToolForcingExtension(pi: ExtensionAPI) {
|
|
27
|
+
let enabled = true;
|
|
28
|
+
|
|
29
|
+
// Comando para ver status e toggle
|
|
30
|
+
pi.registerCommand("ollama-tools-info", {
|
|
31
|
+
description: "Mostra status do Ollama Tool-Forcing e permite toggle",
|
|
32
|
+
handler: async (_args, ctx) => {
|
|
33
|
+
enabled = !enabled;
|
|
34
|
+
const status = enabled ? "✅ ATIVADO" : "❌ DESATIVADO";
|
|
35
|
+
ctx.ui.notify(
|
|
36
|
+
`Ollama Tool-Forcing: ${status}\n\n` +
|
|
37
|
+
`Este modo força modelos abertos a chamar ferramentas antes de parar, ` +
|
|
38
|
+
`evitando gerações vazias que interrompem o loop autônomo do ASM Agent.`,
|
|
39
|
+
"info"
|
|
40
|
+
);
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Injetar instrução no system prompt antes de cada turno
|
|
45
|
+
pi.on("before_agent_start", async (event) => {
|
|
46
|
+
if (!enabled) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Instrução forte para forçar uso de ferramentas
|
|
51
|
+
const toolForceRule = \`
|
|
52
|
+
|
|
53
|
+
## ⚠️ REGRA CRÍTICA: TOOL-FORCING PARA MODELOS LOCAIS
|
|
54
|
+
|
|
55
|
+
Você está rodando num modelo aberto (Ollama/local). Para garantir que trabalhe autonomamente:
|
|
56
|
+
|
|
57
|
+
1. **NUNCA pare sem chamar uma ferramenta**: Depois de pensar/raciocinar, você DEVE chamar
|
|
58
|
+
uma ferramenta (bash, edit, ipython, etc) para continuar o trabalho.
|
|
59
|
+
|
|
60
|
+
2. **Sem gerações vazias**: Não é permitido apenas gerar um "thinking" e depois parar.
|
|
61
|
+
Se terminou um ciclo, chame uma ferramenta para validar ou avançar.
|
|
62
|
+
|
|
63
|
+
3. **Ferramentas = Continuidade**: As ferramentas são como seus "pulmões". Use-as para:
|
|
64
|
+
- Verificar status (bash, ipython)
|
|
65
|
+
- Fazer alterações (edit, bash)
|
|
66
|
+
- Validar resultados (bash com testes)
|
|
67
|
+
- Avançar a tarefa passo a passo
|
|
68
|
+
|
|
69
|
+
4. **Padrão de turno correto**:
|
|
70
|
+
a) Receber tarefa e pensar (thinking)
|
|
71
|
+
b) Chamar uma ferramenta para agir
|
|
72
|
+
c) Observar resultado
|
|
73
|
+
d) Voltar ao passo (a) se precisar avançar
|
|
74
|
+
|
|
75
|
+
5. **Proibido**: Terminar turno com apenas:
|
|
76
|
+
- Uma seção de "thinking" vazia
|
|
77
|
+
- Sem nenhuma tool call
|
|
78
|
+
- Sem texto de resposta
|
|
79
|
+
|
|
80
|
+
Se não tiver nada para fazer, ainda assim chame bash ou ipython para verificar status.
|
|
81
|
+
|
|
82
|
+
ESSA REGRA SE APLICA SEMPRE. Sem exceções. Modelos menores têm tendência de "congelar"
|
|
83
|
+
se deixados livres — use ferramentas constantemente para manter-se "vivo".
|
|
84
|
+
\`;
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
systemPrompt: event.systemPrompt + toolForceRule,
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import unittest
|
|
5
|
+
import logging
|
|
6
|
+
from test.test_bash import BashTest
|
|
7
|
+
|
|
8
|
+
class CustomTestResult(unittest.TextTestResult):
|
|
9
|
+
def addFailure(self, test, err):
|
|
10
|
+
super().addFailure(test, err)
|
|
11
|
+
print("\n=== FAILURE DETAILS ===")
|
|
12
|
+
print(f"Failed test: {test.id()}")
|
|
13
|
+
print(f"Error: {err}")
|
|
14
|
+
|
|
15
|
+
if __name__ == '__main__':
|
|
16
|
+
suite = unittest.TestSuite()
|
|
17
|
+
suite.addTest(BashTest("test_undelivered_kill_leaves_journal_record_active"))
|
|
18
|
+
runner = unittest.TextTestRunner(resultclass=CustomTestResult)
|
|
19
|
+
runner.run(suite)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1121,12 +1121,12 @@ class BashTest(unittest.IsolatedAsyncioTestCase):
|
|
|
1121
1121
|
):
|
|
1122
1122
|
handle = bash("sleep 30")
|
|
1123
1123
|
try:
|
|
1124
|
-
records = await _poll_journal(journal, count=1)
|
|
1124
|
+
records = await _poll_journal(journal, count=1, pid=handle.pid)
|
|
1125
1125
|
self.assertTrue(records[-1]["active"])
|
|
1126
1126
|
with mock.patch.object(bash_module, "_signal_group", return_value=False):
|
|
1127
1127
|
bash_module._kill_live_handles()
|
|
1128
1128
|
await asyncio.sleep(0.2) # give any (wrong) inactive write time to land
|
|
1129
|
-
records = await _poll_journal(journal, count=1)
|
|
1129
|
+
records = await _poll_journal(journal, count=1, pid=handle.pid)
|
|
1130
1130
|
self.assertEqual(len(records), 1)
|
|
1131
1131
|
self.assertTrue(records[-1]["active"])
|
|
1132
1132
|
finally:
|
|
@@ -1260,12 +1260,14 @@ async def _poll_group_dead(pgid: int, timeout: float = 5.0) -> None:
|
|
|
1260
1260
|
raise AssertionError(f"process group {pgid} still alive after {timeout}s")
|
|
1261
1261
|
|
|
1262
1262
|
|
|
1263
|
-
async def _poll_journal(path: str, count: int, timeout: float = 2.0) -> list[dict]:
|
|
1263
|
+
async def _poll_journal(path: str, count: int, timeout: float = 2.0, pid: int | None = None) -> list[dict]:
|
|
1264
1264
|
deadline = asyncio.get_running_loop().time() + timeout
|
|
1265
1265
|
records: list[dict] = []
|
|
1266
1266
|
while asyncio.get_running_loop().time() < deadline:
|
|
1267
1267
|
with open(path) as f:
|
|
1268
1268
|
records = [json.loads(line) for line in f if line.strip()]
|
|
1269
|
+
if pid is not None:
|
|
1270
|
+
records = [r for r in records if r.get("pid") == pid]
|
|
1269
1271
|
if len(records) >= count:
|
|
1270
1272
|
return records
|
|
1271
1273
|
await asyncio.sleep(0.05)
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
oauth_exports,
|
|
53
53
|
runPrintMode,
|
|
54
54
|
runRpcMode
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-GUPU2EEK.js";
|
|
56
56
|
import {
|
|
57
57
|
AgentSession,
|
|
58
58
|
CURRENT_SESSION_VERSION,
|
|
@@ -136,7 +136,7 @@ import {
|
|
|
136
136
|
withFileMutationQueue,
|
|
137
137
|
wrapRegisteredTool,
|
|
138
138
|
wrapRegisteredTools
|
|
139
|
-
} from "./chunk-
|
|
139
|
+
} from "./chunk-NESKY6T7.js";
|
|
140
140
|
import "./chunk-VWR3YFK5.js";
|
|
141
141
|
import "./chunk-N2CSAETN.js";
|
|
142
142
|
import "./chunk-5QCJ5DQU.js";
|
|
@@ -329,7 +329,7 @@ import {
|
|
|
329
329
|
waitForPromptAdmission,
|
|
330
330
|
watchWithErrorHandler,
|
|
331
331
|
wrapTextWithAnsi
|
|
332
|
-
} from "./chunk-
|
|
332
|
+
} from "./chunk-NESKY6T7.js";
|
|
333
333
|
import {
|
|
334
334
|
array,
|
|
335
335
|
boolean,
|
|
@@ -10216,7 +10216,6 @@ __export(oauth_exports, {
|
|
|
10216
10216
|
// dist/core/auth-storage.js
|
|
10217
10217
|
var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
|
|
10218
10218
|
import { chmodSync as chmodSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
10219
|
-
import { homedir as homedir2 } from "os";
|
|
10220
10219
|
import { dirname as dirname3, join as join3 } from "path";
|
|
10221
10220
|
|
|
10222
10221
|
// dist/core/prime-inference-auth.js
|
|
@@ -10895,31 +10894,6 @@ var AuthStorage = class _AuthStorage {
|
|
|
10895
10894
|
})
|
|
10896
10895
|
};
|
|
10897
10896
|
}
|
|
10898
|
-
getAntigravityAuthCandidate(provider) {
|
|
10899
|
-
if (provider !== "google" && provider !== "gemini") {
|
|
10900
|
-
return void 0;
|
|
10901
|
-
}
|
|
10902
|
-
const credsPath = join3(homedir2(), ".gemini", "oauth_creds.json");
|
|
10903
|
-
if (!existsSync2(credsPath)) {
|
|
10904
|
-
return void 0;
|
|
10905
|
-
}
|
|
10906
|
-
try {
|
|
10907
|
-
const data = JSON.parse(readFileSync2(credsPath, "utf-8"));
|
|
10908
|
-
if (data && data.access_token) {
|
|
10909
|
-
return {
|
|
10910
|
-
label: "Antigravity CLI",
|
|
10911
|
-
...this.createAuthSourceCandidate({
|
|
10912
|
-
configured: false,
|
|
10913
|
-
source: "antigravity",
|
|
10914
|
-
identityMaterial: provider,
|
|
10915
|
-
valueMaterial: data.access_token
|
|
10916
|
-
})
|
|
10917
|
-
};
|
|
10918
|
-
}
|
|
10919
|
-
} catch {
|
|
10920
|
-
}
|
|
10921
|
-
return void 0;
|
|
10922
|
-
}
|
|
10923
10897
|
getStoredAuthCandidate(provider, options) {
|
|
10924
10898
|
const credential = this.data[provider] ?? (provider === "google" ? this.data.gemini : provider === "gemini" ? this.data.google : void 0);
|
|
10925
10899
|
if (!credential) {
|
|
@@ -11004,7 +10978,6 @@ var AuthStorage = class _AuthStorage {
|
|
|
11004
10978
|
fallbackCandidate
|
|
11005
10979
|
] : [
|
|
11006
10980
|
this.getRuntimeAuthCandidate(provider),
|
|
11007
|
-
this.getAntigravityAuthCandidate(provider),
|
|
11008
10981
|
this.getStoredAuthCandidate(provider),
|
|
11009
10982
|
this.getEnvironmentAuthCandidate(provider),
|
|
11010
10983
|
fallbackCandidate
|
|
@@ -11327,24 +11300,6 @@ var AuthStorage = class _AuthStorage {
|
|
|
11327
11300
|
};
|
|
11328
11301
|
}
|
|
11329
11302
|
}
|
|
11330
|
-
if (providerId === "google" || providerId === "gemini") {
|
|
11331
|
-
const agyCandidate = this.getAntigravityAuthCandidate(providerId);
|
|
11332
|
-
if (agyCandidate && !this.isAuthSourceStale(providerId, agyCandidate)) {
|
|
11333
|
-
const credsPath = join3(homedir2(), ".gemini", "oauth_creds.json");
|
|
11334
|
-
if (existsSync2(credsPath)) {
|
|
11335
|
-
try {
|
|
11336
|
-
const data = JSON.parse(readFileSync2(credsPath, "utf-8"));
|
|
11337
|
-
if (data && data.access_token) {
|
|
11338
|
-
return {
|
|
11339
|
-
apiKey: data.access_token,
|
|
11340
|
-
sourceToken: this.getAuthSourceTokenForCandidate(providerId, agyCandidate)
|
|
11341
|
-
};
|
|
11342
|
-
}
|
|
11343
|
-
} catch {
|
|
11344
|
-
}
|
|
11345
|
-
}
|
|
11346
|
-
}
|
|
11347
|
-
}
|
|
11348
11303
|
const cred = this.get(providerId);
|
|
11349
11304
|
if (cred?.type === "api_key") {
|
|
11350
11305
|
const storedCandidate = this.getStoredAuthCandidate(providerId);
|
|
@@ -11776,7 +11731,7 @@ function requireCount(args, count, usage) {
|
|
|
11776
11731
|
// dist/core/settings-manager.js
|
|
11777
11732
|
var import_proper_lockfile2 = __toESM(require_proper_lockfile(), 1);
|
|
11778
11733
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
11779
|
-
import { homedir as
|
|
11734
|
+
import { homedir as homedir2 } from "os";
|
|
11780
11735
|
import { dirname as dirname4, join as join4 } from "path";
|
|
11781
11736
|
var RECENT_MODELS_LIMIT = 20;
|
|
11782
11737
|
var DEFAULT_IDLE_EVICTION_MINUTES = 90;
|
|
@@ -12141,10 +12096,10 @@ var SettingsManager = class _SettingsManager {
|
|
|
12141
12096
|
return sessionDir;
|
|
12142
12097
|
}
|
|
12143
12098
|
if (sessionDir === "~") {
|
|
12144
|
-
return
|
|
12099
|
+
return homedir2();
|
|
12145
12100
|
}
|
|
12146
12101
|
if (sessionDir.startsWith("~/")) {
|
|
12147
|
-
return join4(
|
|
12102
|
+
return join4(homedir2(), sessionDir.slice(2));
|
|
12148
12103
|
}
|
|
12149
12104
|
return sessionDir;
|
|
12150
12105
|
}
|
|
@@ -13542,7 +13497,7 @@ async function launchDaemonUpdateRestartCoordinator(options) {
|
|
|
13542
13497
|
import { spawn as spawn2, spawnSync } from "node:child_process";
|
|
13543
13498
|
import { createHash as createHash3 } from "node:crypto";
|
|
13544
13499
|
import { existsSync as existsSync4, mkdirSync as mkdirSync5, readdirSync, readFileSync as readFileSync5, rmSync as rmSync3, statSync, writeFileSync as writeFileSync5 } from "node:fs";
|
|
13545
|
-
import { homedir as
|
|
13500
|
+
import { homedir as homedir3, tmpdir } from "node:os";
|
|
13546
13501
|
import { basename as basename3, dirname as dirname6, join as join6, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
|
|
13547
13502
|
|
|
13548
13503
|
// ../../node_modules/glob/dist/esm/index.min.js
|
|
@@ -18495,7 +18450,7 @@ function toPosixPath(p) {
|
|
|
18495
18450
|
return p.split(sep2).join("/");
|
|
18496
18451
|
}
|
|
18497
18452
|
function getHomeDir() {
|
|
18498
|
-
return process.env.HOME ||
|
|
18453
|
+
return process.env.HOME || homedir3();
|
|
18499
18454
|
}
|
|
18500
18455
|
function prefixIgnorePattern(line, prefix) {
|
|
18501
18456
|
const trimmed = line.trim();
|
|
@@ -20320,7 +20275,7 @@ var DefaultPackageManager = class {
|
|
|
20320
20275
|
var import_proper_lockfile4 = __toESM(require_proper_lockfile(), 1);
|
|
20321
20276
|
import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
|
|
20322
20277
|
import { existsSync as existsSync5, mkdirSync as mkdirSync6, readdirSync as readdirSync2, readFileSync as readFileSync6, realpathSync, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "node:fs";
|
|
20323
|
-
import { homedir as
|
|
20278
|
+
import { homedir as homedir4 } from "node:os";
|
|
20324
20279
|
import { basename as basename4, dirname as dirname7, join as join7, resolve as resolve4 } from "node:path";
|
|
20325
20280
|
var DAEMON_SUPERVISOR_REGISTRY_DIR_ENV = "ASM_AGENT_INTERNAL_DAEMON_SUPERVISOR_REGISTRY_DIR";
|
|
20326
20281
|
var OWNER_VERSION = 1;
|
|
@@ -20509,7 +20464,7 @@ var DaemonShutdownAdmission = class {
|
|
|
20509
20464
|
}
|
|
20510
20465
|
};
|
|
20511
20466
|
function defaultDaemonSupervisorRegistryDir(environment = process.env) {
|
|
20512
|
-
return environment[DAEMON_SUPERVISOR_REGISTRY_DIR_ENV] ?? join7(
|
|
20467
|
+
return environment[DAEMON_SUPERVISOR_REGISTRY_DIR_ENV] ?? join7(homedir4(), ".asm-agent", "supervisor-owners");
|
|
20513
20468
|
}
|
|
20514
20469
|
function legacyDaemonSupervisorRegistryDir(environment = process.env) {
|
|
20515
20470
|
return environment[DAEMON_SUPERVISOR_REGISTRY_DIR_ENV] ? void 0 : resolve4(defaultDaemonSocketDir(), "supervisor-owners");
|
|
@@ -26930,7 +26885,7 @@ File: ${modelsJsonPath}`);
|
|
|
26930
26885
|
|
|
26931
26886
|
// dist/core/resource-loader.js
|
|
26932
26887
|
import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync3 } from "node:fs";
|
|
26933
|
-
import { homedir as
|
|
26888
|
+
import { homedir as homedir5 } from "node:os";
|
|
26934
26889
|
import { join as join10, resolve as resolve8, sep as sep4 } from "node:path";
|
|
26935
26890
|
function resolvePromptInput(input, description) {
|
|
26936
26891
|
if (!input) {
|
|
@@ -27406,11 +27361,11 @@ var DefaultResourceLoader = class {
|
|
|
27406
27361
|
const trimmed = p.trim();
|
|
27407
27362
|
let expanded = trimmed;
|
|
27408
27363
|
if (trimmed === "~") {
|
|
27409
|
-
expanded =
|
|
27364
|
+
expanded = homedir5();
|
|
27410
27365
|
} else if (trimmed.startsWith("~/")) {
|
|
27411
|
-
expanded = join10(
|
|
27366
|
+
expanded = join10(homedir5(), trimmed.slice(2));
|
|
27412
27367
|
} else if (trimmed.startsWith("~")) {
|
|
27413
|
-
expanded = join10(
|
|
27368
|
+
expanded = join10(homedir5(), trimmed.slice(1));
|
|
27414
27369
|
}
|
|
27415
27370
|
return resolve8(this.cwd, expanded);
|
|
27416
27371
|
}
|
|
@@ -40328,7 +40283,7 @@ var PrimeTeamSelectorComponent = class extends Container {
|
|
|
40328
40283
|
|
|
40329
40284
|
// dist/modes/interactive/auth-flows.js
|
|
40330
40285
|
var BEDROCK_PROVIDER_ID = "amazon-bedrock";
|
|
40331
|
-
var ASM_AGENT_LOGIN_PROVIDER_IDS = /* @__PURE__ */ new Set(["openai-codex", "anthropic"
|
|
40286
|
+
var ASM_AGENT_LOGIN_PROVIDER_IDS = /* @__PURE__ */ new Set(["openai-codex", "anthropic"]);
|
|
40332
40287
|
var ANTHROPIC_SUBSCRIPTION_AUTH_WARNING = "Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage.";
|
|
40333
40288
|
function isAnthropicSubscriptionAuthKey(apiKey) {
|
|
40334
40289
|
return typeof apiKey === "string" && apiKey.startsWith("sk-ant-oat");
|